Writing systems you can actually hand over
Handover is not a deliverable at the end of a project — it is a design constraint from the start. What we have learned building systems that outlive the team that built them.
Most software rewrites are not triggered by a technical problem. They are triggered by a handover that never happened.
A team ships something ambitious. They move on. The new team inherits a codebase they did not write, with no written record of the decisions behind it. Every small change feels dangerous. Eventually someone proposes a full rewrite — because it is easier to write something than to read it.
We have been on both sides of this. Here is what we do differently now.
Handover is a design constraint, not a closing task
The test is simple: can a modest team, with no prior context, safely operate and extend this system six months after we leave? If that question makes you nervous, you have more handover work to do — regardless of whether the feature list is done.
This pushes real design decisions upstream:
- Technology choices. Boring technology with a large community is a handover feature. A niche framework chosen for elegance becomes a hostage negotiation in year three.
- Code structure. We favour modules organised by domain, not by technical layer. A new engineer can land in a domain and find everything related to it in one place.
- Observability. If an operator cannot see what the system is doing in production, they cannot keep it healthy. Instrumentation is part of the initial build, not a later ticket.
Write the decisions down as you make them
The most important artifact we produce is not code. It is a series of short written decisions.
Each one captures:
- The decision (one sentence).
- The alternatives we considered.
- The reason we chose this over the others.
- The constraints or assumptions behind the decision.
These live in the repository as plain markdown, reviewed like code. We do not rewrite history; if a decision is later reversed, we write a new note that supersedes the old one.
This sounds bureaucratic. It is the opposite. It is what lets you stop re-arguing the same question every six months.
Design for the second team, not the first
The team who builds a system is never the team who operates it for a decade. Everything we build, we build for the second team.
What does that mean in practice?
- READMEs that answer real questions — how to run locally, how to deploy, where state lives, how to recover from the three most likely failure modes.
- Runbooks for things that break. Not a wiki of theoretical procedures — short notes for the incidents that have actually happened, updated each time they happen again.
- Tests that describe behaviour, not implementation. A test suite that fails when business rules change is signal. One that fails when you rename a variable is noise.
Make the exit story a deliverable
On every engagement we include an explicit, contractual deliverable: a handover readiness report. At the end of the work, a senior engineer who was not on the build spends a week operating the system using only its documentation. They file a short report on what worked and what did not.
Whatever they could not figure out, we fix before we leave.
This is not an abstract kindness. It is the single most effective way we have found to make sure the work we do stays done.