Continuous Refinement
Improve in small, reversible steps, and delete what you replaced.
Overview
Systems decay without care. We improve them in small, safe increments rather than betting on a risky rewrite, and a change is finished when the thing it replaced is gone.
Why
The default outcome of a rewrite under time pressure is that the old path survives "just in case", and now there are two. A refactor that does not delete something has usually increased the number of ways the system can be wrong. Inherited code is the harder half. It breaks nothing, so nothing prompts anyone to look at it, and its cost stays invisible until you try to change or share it. Keeping it is a decision being made silently every day, and the only way to bound it is to name the category and periodically ask what belongs in it. Care is also not evenly distributed. Two halves of one problem, solved months apart, can differ enormously in rigour without anyone noticing, because nothing ever puts them side by side.
Standard
Change in increments small enough to verify one at a time, and verify each against the previous behaviour before starting the next. Treat the deletion as the completion criterion, not the addition. A capability shipped alongside the thing it was meant to replace is not finished, and a deletion that has not been committed has not happened. Ask of inherited code: did we decide this, or did we receive it? Received code is held to a different standard, because keeping it is an active choice. Find the neglected half of a system by placing it next to your best work and asking why they differ.
What This Forbids
- Keeping the old path "just in case" after shipping its replacement
- Counting a capability as delivered before the code it replaces is gone
- Treating a rewrite as safer than a sequence of verified increments
- Treating inherited code as neutral because it has not broken anything
Examples
- A storage layer rebuilt rather than extended, deleting the two inherited services it replaced. Separately, an observability layer that deleted the inherited interceptor rather than running both.
- A shared foundation adopted across six independent increments, each verified against the previous behaviour before the next began. Three of the six required no change to the shared code at all, which was the first real signal that its shape was right.
- One project deleted an entire directory of duplicated infrastructure after adopting the shared version. Across a family of four codebases, that single deletion was the only proof that sharing worked at all, and it was enough to reframe the whole strategy.
Counter Examples
- A codebase with four months and hundreds of commits of genuinely good patterns, none ever extracted, which then went dormant. Its ideas survived only because another project copied the files by hand, and two of its capabilities were lost entirely, then independently rebuilt seven weeks later, without them.
- One table carrying three representations of a single concept: an inherited shape, a better shape adopted halfway, and a third produced by a helper with no callers anywhere. Nothing broke. That is exactly why nobody noticed.
- A URL utility reassembling paths by reading four environment variables and performing pattern surgery, still running long after the layer that made it necessary had been replaced.
Lessons Learned
- Adoption is not the same as less code. One migration removed around 145 lines of duplicated metadata and added around 550 of declaration and presentation. The payoff was one authority per rule and four latent defects surfaced, not a smaller codebase. Claiming a line-count win would have taught the wrong lesson.
- A deletion in an uncommitted working tree is not a deletion.
- Care is not evenly distributed. The same problem was solved twice in one codebase, once with types, defaults, validation and its own command, once with bare accessors and no schema, and the asymmetry was invisible until the two were placed side by side. That asymmetry, not either half, was the finding.
- Residue accumulates silently and is invisible in production. It becomes visible the moment you try to share the code with something else.
- Metadata that nothing consumes rots. Four declared values turned out to be quietly wrong, and all four were found only when something finally read them.
Learned From
- DentraA storage layer rebuilt with its predecessors deleted; residue named as a category; care asymmetry found only by placing two halves of one problem together.
- JinaraThe only completed adoption lifecycle in the family, adopt the shared version, then delete the duplicate.
- AtlasWhat happens when the loop stays open: patterns invented, never extracted, then dormant, and two capabilities lost outright.
- ZothKitSix verified increments, and honest accounting when adoption grew the codebase rather than shrinking it.
Related Principles· 2
Related Documents· 2
Referenced By· 4
Version History
- v0.1.0
Principle established with placeholder content.
- v0.2.0strengthened
Written from real experience. Absorbed the completion criterion, a change is finished when what it replaced is deleted, plus residue as a named category and the uneven distribution of care.