Bus factor is the number of people who can leave before a test station can no longer be rebuilt or changed. On most production floors that run LabVIEW, that number is one. You can measure it in an afternoon, and the fixes range from a written hand-over to a rewrite in text under Git.
What Bus Factor Means for a Test Station
The term comes from software: how many people would have to get hit by a bus before the project stalls. For a test station the question is more concrete. If the person who built it left tomorrow, could someone else open the sequence, change a limit, and redeploy it by Friday?
Three things have to be true for the answer to be yes. Someone else can read the source, and someone else has a licence and a machine that can build it. And the results live somewhere the team can see them, not on the station's local drive.
The count of people who satisfy all three is the bus factor. A station with a bus factor of one isn't broken, it's just a single point of failure that happens to still be working.
Audit It in an Afternoon
You don't need a tool. Walk the floor with a spreadsheet and fill one row per station.
station,product,runs_per_week,tool,who_can_open,who_can_rebuild,last_changeFCT-01,controller-pcba,420,LabVIEW 2020,marc,marc,2024-11-03FCT-02,controller-pcba,380,LabVIEW 2020,marc,marc,2024-11-03EOL-01,drone-assembly,150,TestStand 2019,marc;priya,marc,2023-06-18BURN-01,battery-module,90,Python,priya;tom;lena,priya;tom;lena,2026-08-21CAL-01,imu-module,60,LabVIEW 2018,marc,,2021-02-09Two columns carry the answer. who_can_open lists everyone who can read the source and say what it does. who_can_rebuild lists everyone who has the licence, the right toolchain version and enough understanding to produce a new deployable. Separate names with semicolons. Leave the cell empty when the honest answer is nobody.
Be strict about who_can_rebuild. "Priya once watched Marc do it" doesn't count. If the toolchain version is no longer installed anywhere, the cell is empty even if Marc is still on payroll.
This script reads the file and prints the stations at risk, highest volume first.
bus_factor.py20 lines
# Prints the stations that one person (or nobody) can rebuild, highest volume first.import csvdef people(cell): return {name.strip() for name in cell.split(";") if name.strip()}with open("stations.csv", newline="") as f: rows = list(csv.DictReader(f))at_risk = []for row in rows: rebuilders = people(row["who_can_rebuild"]) if len(rebuilders) <= 1: at_risk.append((int(row["runs_per_week"]), row["station"], rebuilders))for runs, station, rebuilders in sorted(at_risk, reverse=True): who = ", ".join(sorted(rebuilders)) or "nobody" print(f"{station:8} {runs:5d} runs/week bus factor {len(rebuilders)} ({who})")For the sample file it prints FCT-01, FCT-02 and EOL-01 at bus factor 1, all on Marc, and CAL-01 at bus factor 0. That's also your fix order: the station with the most runs per week and the fewest rebuilders goes first.
Most plants learn their bus factor the same week they learn about a notice period. The afternoon audit is cheaper.
What Drives It Down
None of these are mistakes. They're the defaults of the toolchain and the way small teams grow, and each one quietly removes a name from who_can_rebuild.
| Driver | Why it lowers the number | How you notice |
|---|---|---|
| Binary VIs | A .vi file diffs as a blob. Nobody can review a change without opening it in the same LabVIEW version. | Pull requests don't exist. Changes are "Marc sent me the new build". |
| Vendor language | LabVIEW is capable, and fewer engineers learn it each year. The pool of people who can read the source shrinks on its own. | Job postings stay open for months. |
| Licences tied to a named seat | Since 2023 LabVIEW and TestStand are subscription only. The seat, the build machine and the person are usually the same thing. | Deactivating a leaver's account also deactivates the ability to build. |
| Undocumented fixtures | The pogo pin map, the relay matrix and the "don't touch DIP switch 3" knowledge live in one head. | The fixture drawing is a photo on someone's phone. |
| Results in local TDMS or CSV files | Yield and Cpk data sits on the station's drive. The only person who can answer "what's FPY this month" is the one who wrote the report VI. | Management asks for a yield number and waits two days. |
Two of these (vendor language and named seats) are structural and you can't fix them inside the toolchain. The other three you can, which is what the ladder below is about.
The Mitigation Ladder
Each level is a real step and each one costs something. Pick the level you can hold, then climb when you have the budget.
| Level | Action | Bus factor after |
|---|---|---|
| 0 | Nothing. One engineer, binary VIs, results on the station. | 1 |
| 1 | A hand-over document per station and a build machine a second person can log into. | 1, but recovery takes days instead of months |
| 2 | Source under Git, a second seat licence, and one other engineer performs a full rebuild from scratch once. | 2 |
| 3 | Results pushed off the station to TofuPilot through the REST API, so yield, Cpk and per-unit history stop depending on the report VI. | 2, and the data outlives the station |
| 4 | Station rewritten as text (procedure, phases, plugs) in Python under Git, results in TofuPilot. | Everyone on the team who reads Python |
Level 1 is what what to do when your LabVIEW engineer leaves covers, and document a test station for hand-over has the template. Do it this week for every station at bus factor 1. It doesn't change the number, it changes how long you're down.
Level 2 is where most teams stall. The second seat costs a subscription, and the second engineer has to be someone who can read LabVIEW, which brings you back to why LabVIEW engineers are hard to hire.
Level 3 needs no rewrite. A LabVIEW station can POST each run to https://www.tofupilot.app/api/v2/runs from the HTTP Client VIs with the outcome, serial number and measurements. The station stays as it is, but the data is now something the whole team sees.
Why Text, Git and a Shared Platform Is the Durable Fix
Levels 1 to 3 patch the problem. They decay: the hand-over doc drifts, the second seat lapses, the second engineer moves to another project. Level 4 changes what the station is made of, and that's why it holds.
Three properties do the work.
The station is text. A procedure.yaml plus a folder of phases/*.py and plugs/*.py diffs line by line. Anyone who reads Python can review a limit change in a pull request, and Git keeps the history of every change and who approved it.
The toolchain is free and installed everywhere. There's no named seat to lapse, and a laptop with Python and the TofuPilot CLI can rebuild any station in the repo.
The results are shared. Every run lands in TofuPilot with its measurements, so FPY, Cpk and the failure Pareto are on a dashboard the whole team opens, not in a TDMS file only one person can parse.
Here's what "reviewable in a pull request" looks like. A reviewer who has never seen the station can tell you what it tests and what the limits are in under a minute.
procedure.yaml34 lines
# Controller PCBA functional test. Limits are the review surface.name: Controller PCBA FCTversion: 2.4.0unit: serial_number: default_value: SN-000000 part_number: default_value: CTRL-PCBA-Aplugs: - name: psu python: plugs.psu:PowerSupply - name: dmm python: plugs.dmm:Multimetermain: - name: Power Rails python: phases.power_rails measurements: - name: rail_3v3 unit: V validators: - operator: ">=" expected_value: 3.2 - operator: "<=" expected_value: 3.4 - name: rail_5v unit: V validators: - operator: ">=" expected_value: 4.85 - operator: "<=" expected_value: 5.15When the 3.3 V limit needs to widen to 3.15 V, the diff is one line. The reviewer sees the old value, the new value and the author. That's the whole mechanism, and it's the reason the bus factor stops being a property of one engineer and becomes a property of the team.
The bus factor after level 4 isn't infinite. Someone still has to know the fixture and the product. But the knowledge that used to be locked in a binary is now in a file anyone can open, and that's the part that used to walk out the door.
Start With One Station
Take the station your audit put at the top: the highest volume with the fewest rebuilders. Rebuild it in Python with the TofuPilot Framework, keep the same limits, and run it side by side with the LabVIEW version on the same units for two weeks. When the two agree, switch the operators over and update the CSV.
# Install the CLI, then run the station locally (no account needed) or with upload.curl -fsSL https://www.tofupilot.app/install | shtofupilot run ./procedure.yamltofupilot run ./procedure.yaml --uploadThe step-by-step rebuild is in how to migrate from LabVIEW to Python for manufacturing tests with TofuPilot, and the framework itself is at tofupilot.com/products/framework. The Lab tier is free, and tofupilot run works without an account.
