I’m Tired of Calling Solo Development a Bus-Factor Problem
After 47 TestFlight builds, I care less about replacing myself than making yesterday’s decisions recoverable.
I’m Tired of Calling Solo Development a Bus-Factor Problem
After 47 TestFlight builds, I care less about replacing myself than making yesterday’s decisions recoverable.
I am tired of hearing that a solo developer has a bus factor of one. I understand the arithmetic. After 47 TestFlight builds, a seven-day release with six fixes, and one regression I caused myself, I also think the phrase points at the wrong emergency.
Nobody is waiting to replace me tomorrow. The person most likely to inherit my unfinished work is me, three weeks older, carrying a different problem in working memory.
That is the handoff I kept failing.
I would open Xcode, recognize every filename, and still have no idea why I had rejected the obvious implementation. I would see a defensive branch and wonder whether it guarded a real failure or an imaginary one. I would find a draft with a confident title and no record of the evidence that made it worth writing. Ownership was intact. Context was gone.
So here is the short version:
- Solo work does not remove handoffs. It moves them through time.
- A comment explains code; a recovery record explains a decision, its evidence, and the next safe move.
- I now judge tools by how well they let future me resume after an interruption, not by how organized they make present me feel.
- The goal is not to document everything. It is to preserve the expensive parts of forgetting.
I am not building a miniature bureaucracy for one person. I am building a trail back into the work.
Why does “bus factor” bother me?
The phrase turns continuity into a staffing question: how many people could disappear before the project stopped? That is useful for a company. It is less useful at a desk where the head count will still be one next month.
My recurring failure is not disappearance. It is context eviction.
On one release, version 3.1, six fixes moved through a single seven-day queue. Development, testing, support, writing, and release work could not happen in parallel because I was the same person doing each job. I introduced one regression while fixing something else. The build eventually reached 99.81 percent crash-free, but the week did not feel like uninterrupted ownership. It felt like seven handoffs between roles that happened to share one body.
The dangerous moment came each time I changed hats.
Developer-me knew why a branch existed. Tester-me only saw the behavior. Writer-me wanted a clean explanation. Release-manager-me wanted the smallest possible change. When I returned to the code, developer-me had to reconstruct the decision from residue.
That residue was often terrible: a commit named “fix,” a checkbox marked done, or a code comment that described what the line already said.
The bus-factor framing made me imagine a stranger reading the repository. The more practical question was harsher: could I resume this work after lunch?
What makes work recoverable rather than merely documented?
I use a four-part test now. A useful recovery record preserves the trigger, the rejected option, the observed result, and the next boundary.
RecordWhat it preservesWhat it usually losesCode commentA local constraintWhy this option beat anotherCommit diffWhat changedThe failed path before the changeTask checkboxCompletion stateEvidence and uncertaintyRecovery noteTrigger, rejection, result, next moveMost implementation detail
The recovery note is not a replacement for the other three. It is the bridge between them.
One of my most embarrassing bugs came from Apple’s mail composer. I spent 41 minutes moving between Xcode, the simulator, and a real-device run. My activity log counted 28 app switches. The fix changed six lines, four of which were whitespace.
A diff could show the six lines. It could not show the important conclusion: MFMailComposeResult.sent did not prove that a message had been delivered. It only described the composer’s UI outcome. I eventually renamed my internal state to queued because the word sent carried a promise the API could not verify.
That conclusion is the recoverable asset. The six-line patch is only its address.
I wrote about the distinction later in a teardown of those four composer results. The act of explaining it exposed another gap: I had remembered the callback race but nearly forgotten why the product language changed. A recovery trail has to connect code semantics to user-facing semantics or the next edit can undo the lesson without touching the bug.
The same rule changed how I treat background delivery. The outbox in my app uses atomic file writes, a background URLSession, and retry backoff. Apple’s background transfer documentation explains what the platform can continue after the app is suspended. It cannot preserve why I chose a particular retry boundary or what the screen should claim before the server responds.
For that, I need one sentence beside the implementation: the interface may clear after durable local storage, but it must not display a delivery claim before the remote result exists.
That sentence has survived more context switches than any diagram I made.
I overbuilt the first version
My first attempt at continuity was a template with eleven fields.
It asked for the problem, hypothesis, alternatives, risks, decision, date, owner, status, dependencies, rollback plan, and retrospective date. The owner field was especially comic. It was always me.
I completed the template twice. Then I started creating empty copies and telling myself I would fill them after the code worked. The system produced evidence of intention, not evidence of decisions.
I made the same mistake with task tracking. I once kept 2,118 lines in one plain-text task file. Seventy-one percent were marked done, 541 remained open, and 213 were more than six months old. The file was honest in a way a polished dashboard was not: old work did not disappear when I stopped looking at it. But honesty alone did not make the work resumable.
A task named “fix retry” could sit below the fold for months and preserve almost nothing. A smaller note — “do not retry cancelled; preserve the draft; reproduce on iOS 18.1” — could restart the work in seconds.
That plain-text story later received 20 claps and one written response, still the clearest reader signal in my recent history. Dave Crumbine told me he uses TaskPaper and was thinking about the “water line,” the boundary below which tasks disappear from attention. His response sharpened my own problem. Recoverability is not just keeping an item above the water line. It is making sure the item still contains oxygen when it resurfaces.
I now keep the record much smaller:
Trigger: what made me stop and choose? Rejected: what tempting option did I decline, and why? Verified: what did I actually observe? Resume: what is the next safe action?
Four prompts are enough. If I cannot fill them in two minutes, the decision is probably too broad or I am writing a retrospective instead of a handoff.
The rollback path is part of the product
Solo developers are often told to move fast because coordination is cheap. The advice is half right. I can approve my own change immediately. I also have nobody beside me to remember the previous stable state when the change goes wrong.
That makes rollback less of an operations feature and more of a memory feature.
I felt this during the Xcode 16 transition. The app had no third-party Swift packages, an IPA around 4.1 MB, and 47 TestFlight builds in its history. Avoiding dependencies had cost me two extra weekends earlier. During the toolchain transition, I estimated it saved more than 30 hours because there was no package compatibility graph to unwind.
The lesson was not “dependencies are bad.” I would relax the rule for another product. The useful lesson was that every dependency spends some future recovery budget. Sometimes that is a good trade. Pretending the budget does not exist is not.
Git already gives me the mechanical history. Its own documentation describes commits as snapshots, which is exactly what I want from the repository. But a snapshot does not say which human expectation made the change safe. I pair the commit with the smallest possible recovery note and keep rollback instructions close to the state they restore.
Here is the trade-off I leave deliberately open: I may be under-investing in external documentation. A private recovery trail works for future me, but it does not make the project easy for a contributor, an acquirer, or someone responding to an emergency. I accept that gap today because I am optimizing for the handoff that happens every week, not the hypothetical handoff that may never happen.
That choice deserves review. It should not quietly harden into identity.
What I record now
I record boundaries, not narratives.
When a state name overclaims what I know, I record the weaker name and the observation behind it. When a background action can outlive the interface, I record the durable point after which clearing the screen is safe. When I reject a library, I record the cost I am avoiding and the cost I am accepting. When a release crosses several roles, I leave a resume line before changing roles.
I do not record routine syntax, links I can search again, or a diary of every command. Search is cheap. Reconstructing a rejected alternative is expensive.
The distinction has changed my definition of “done.” A change is not done when the code compiles. It is done when I can state what happened, what I ruled out, and where I would restart if tomorrow’s build contradicts me.
That standard is slower at the end of a task and much faster at the beginning of the next one.
It also makes mistakes less dramatic. A regression with a known boundary is a repair. A regression without context feels like an accusation from a stranger who used my keyboard.
FAQ: how much process is enough for one person?
Do I write a recovery note for every change?
No. I write one when the work contains a choice future me is likely to reopen: a rejected dependency, a state-machine boundary, a retry rule, a privacy claim, or a result that looked like success but was not. Renaming a local variable does not need a ceremony.
Where do I keep the notes?
As close as possible to the decision. Some belong in commit messages, some beside a state file, and some in the task record that triggered the work. I care more about a reliable retrieval path than one universal container. A beautiful notebook I forget to open is not continuity.
Is this just documentation with a new name?
Partly. The difference is the test. Documentation can explain a system to a reader. A recovery record must let a specific interrupted person take the next safe action. If it cannot change what I do on resumption, it is background material.
What would make me abandon this approach?
A second regular contributor would change the audience and probably require more explicit setup, ownership, and review records. A tool that could reconstruct rejected alternatives from code and discussion without inventing certainty would also reduce the need. I have not found that tool.
I still dislike the bus-factor phrase, but I no longer dismiss the fear underneath it. Solo work is fragile when its continuity lives only in the current contents of one person’s head. My answer is not to pretend I can replace that person. It is to make yesterday’s judgment recoverable by tomorrow’s version of him.
If you work alone, what is the smallest record that has actually helped you resume — and what “documentation” have you learned to stop writing?
I build a small iOS app alone and write about the decisions that survive after the code is closed. The honest failures are usually the useful part.
메타데이터
- post_id
- bb473b69c302
- slug
- im-tired-of-calling-solo-development-a-bus-factor-problem-bb473b69c302
- url
- https://medium.com/@simplememo.com/im-tired-of-calling-solo-development-a-bus-factor-problem-bb473b69c302
- canonical_url
- https://medium.com/@simplememo.com/im-tired-of-calling-solo-development-a-bus-factor-problem-bb473b69c302
- author_url
- https://medium.com/@simplememo.com
- status
- ok
- fetched_at
- 2026-09-02 01:27:12