Maintenance grew until it consumed 40, 60, sometimes 80% of the team's capacity. You are not alone, and the culprit is not a lack of QA, seniority, or tooling either. Maintenance is the bill that arrives when the flow was skipped. Every question that did not become a spec before the code becomes a ticket afterward. Every coupling that did not become a plan before the PR becomes an incident in production.
This post explains how the Spec, Plan, Ship cycle redirects the work to the window where a defect is still cheap, and what to do with the maintenance liability you already carry today.
1. Maintenance is the liability of an absent flow
There is a time window in which each defect costs between 1x and 5x to fix: before the code exists. After the merge, the same defect costs 10x. After the deploy, 50x. When it becomes recurring maintenance, it becomes a permanent liability, with compound interest in man-hours.
Teams with high maintenance share one trait: the work that should happen before the code (specifying a rule, mapping coupling, defining observability) is happening afterward, in the form of a ticket, an incident, and an emergency refactor. It is not an execution failure, it is a window failure. The team is not executing poorly, it is executing at the wrong moment on the timeline.
The practical consequence is cruel: the more maintenance, the less time is left for the flow that would reduce maintenance. It is a negative feedback loop, and it only breaks with a structural decision to redirect the work to the cheap window.
2. Spec: cut the ambiguity before the code exists
A spec is not a document, it is the gate that stops a question from becoming a bug. A good spec defines three things: invariants (what must always be true), named error cases (what to do when X happens) and contracts (input, output, side effects). If these three pieces are written down, the developer has no room for 'oh, I thought it worked this way'.
What does NOT go into the spec: implementation. A spec describes the problem, not the solution. If you are writing which ORM to use or where to put the cache, you stopped specifying and started designing. Mixing the two couples the business rule to the technology, and creates rework at the first migration or stack change.
Practical heuristic: if a PR opens a discussion about 'what should happen if X', the spec of that feature failed at point X. It is not a personal failure, it is data. Flag it, adjust the spec, and move on. A spec is alive, it is not an audit deliverable that no one rereads after approval.
3. Plan: attack the coupling before the diff
A plan is the map of the change's blast radius. Which files will be touched, in what order, what can break laterally, where the point of no return is. A good plan answers before the code: 'if this PR goes wrong, what comes back with it?'
Without a plan, a refactor is blind surgery. With a plan, it is a checklist. The practical difference: a refactor without a plan generates 3 to 5 maintenance incidents in the weeks following the deploy, because invisible couplings were stepped on without warning. A plan makes those couplings visible BEFORE they become a ticket, in a 20-minute session with coffee, not in a war room at 3 in the morning.
A plan does not need to be long. Five lines that list: files touched, order of change, lateral risks, telemetry to add, rollback condition. Anyone who cannot write those five lines has not understood the problem yet, and will find that out in production, with an audience.
4. Ship: it is a gate, not a goal
Ship is the most mistreated of the three steps. Ship is not 'the deploy went out'. Ship is the delivery with fallback, telemetry and rollback. If you cannot turn the feature off in 2 minutes, you did not ship, you left a future problem with a pretty name.
Invisible maintenance is born here. A feature without an off switch demands an emergency fix in production when things go bad. A feature without telemetry does not report its own failure, and you only find out through an irritated user's ticket, 3 weeks later, when the damage is already spread. A feature without a documented rollback forces the team to invent a procedure under pressure. All of this becomes maintenance man-hours the following month, peaking at the end of the sprint.
Minimum ship gate: (1) a flag or kill switch, (2) a feature health metric exposed on a dashboard, (3) a one-line rollback plan. If any of the three is missing, the ship is debt disguised as delivery.
5. Mitigate the maintenance that already exists
The Spec, Plan, Ship flow solves the future liability. But you already have a liability in the present, and trying to pay it off all at once is what kills teams. Inherited maintenance needs triage, not an all-hands push.
Triage across three dimensions: frequency (how many times the ticket reappears per month), unit cost (how much time each occurrence consumes) and volatility (how often the related code changes). The 'load-bearing walls' of the liability are the ones that score high on all three. Those go into an eradication sprint, with a retroactive spec written BEFORE the refactor. The rest becomes a standardized hotfix or is accepted as an operating cost until the next review.
The classic mistake is trying to zero out the maintenance backlog. You cannot, and trying generates more maintenance (an emergency refactor creates new bugs in already fragile code). The target is to reduce the inflow, not to empty the queue. The inflow drops when spec and plan enter the cycle. The queue shrinks as a consequence, in months, not in weeks.
6. Metrics that show the flow is working
Three signals you can measure in any team, without a new tool, with a spreadsheet:
- Bugs per delivery: bugs reported in production in the 2 weeks after the deploy, divided by the number of features delivered. A downward trend means the spec is working.
- Percentage of tickets that require a retroactive spec: how many tickets reveal 'nobody knew it was supposed to work this way'. A downward trend means the spec cycle is maturing.
- Time to the first post-deploy fix: if it is dropping, the ship's observability is catching things before the user complains, and reactive maintenance is turning proactive.
This is not dashboard vanity. These are the only three numbers that tell you whether the flow is cutting maintenance or just rearranging work from one place to another.
7. The reframe
Maintenance does not disappear. But it stops being destiny. The difference between a team that lives in firefighting mode and a team that ships features is where the work is allocated on the timeline: before the code or after the deploy. It is not talent, it is not the stack, it is not the methodology of the moment. It is the window.
Spec, Plan, Ship is not a methodology, it is a spending decision: you pay now, with 5 lines of spec and 5 lines of plan, or you pay 10x later, in maintenance man-hours. The flow is the redirector. Whoever ignores it keeps paying the bill that arrives every month labeled 'unexpected', and swearing that next time there will be time to do it right.