There is a version of software development that looks like progress and is not. A feature ships every few days, the demo improves every week, and for a while the graph only goes up. Then the third integration arrives, or the first real customer with real data, and the team discovers that the shape of the system cannot accommodate it. The work of the next six months is undoing the work of the last three.
We have chosen the opposite trade. Our projects start slowly on purpose.
Speed is borrowed against a decision you have not made yet
Most early velocity comes from deferring decisions rather than making them. You do not choose a data model, you accept the one your first screen implies. You do not define a boundary between two concerns, you let them share a file and promise to separate them later. Each deferral is a small loan, and the interest is paid in every subsequent change.
The loans that hurt are not the ones people warn about. Duplicated markup is cheap to fix. A confusing variable name is cheap to fix. What is not cheap:
- The data model. Every screen, export, permission check and integration eventually depends on the shape of your core entities. Getting it wrong does not produce one bug, it produces a category of bugs that reappears for years.
- Boundaries between systems. Once two concerns know each other's internals, separating them requires touching both. The cost of that separation grows with every feature built across the seam.
- The meaning of a word. If "account" means one thing in billing and another in reporting, the ambiguity will be encoded into a hundred functions before anyone names the problem.
None of these are recoverable with a refactor sprint. They are recoverable with a rewrite, which is the most expensive way to learn something you could have learned by thinking for a week.
What the slow start actually consists of
Building slowly is not the same as building carefully in a general sense. It is a specific set of activities, and they happen before there is much to look at.
We spend the first phase trying to state the problem in one sentence. Not the solution, the problem. This is harder than it sounds and it is where most products quietly fail. A problem you cannot state precisely is a problem you will solve approximately, and an approximate solution to an unclear problem is indistinguishable from a product nobody needs.
Then the model. We describe the entities, their relationships and their invariants before designing a screen, because the screens are downstream of the model and not the other way around. If the model is right, interfaces are inexpensive to change and can keep improving for years. If the model is wrong, no amount of interface work will hide it.
Then the boundaries. We decide what each part of the system is allowed to know, and we write that down. Boundaries are the only defence against the failure mode where a codebase becomes technically functional and practically unmodifiable.
Only then does implementation start, and implementation is comparatively fast, because the expensive questions have already been answered.
The measurement problem
The honest difficulty with this approach is that it is hard to demonstrate while it is happening. A team that spends three weeks on a data model has nothing to show at the end of it except a document and some type definitions. A team that spends three weeks building screens has screens.
The difference appears later, and it appears as an absence. There is no rewrite. There is no month spent migrating data because a column meant two things. There is no feature that turns out to be impossible. Absences are difficult to put in a progress report, which is exactly why the incentives in most companies point the other way.
We accept that cost because we own what we build. Nobody is asking us to show a graph this quarter, so we can optimise for the state of the system in year five rather than the state of the demo in week three.
Where speed belongs
This is not an argument for slowness as a virtue. Slowness in the wrong place is just waste.
Once the model and the boundaries are settled, we want to move very fast, and the point of the slow start is that we can. Well-defined systems are pleasant to extend. Adding a feature is a local change rather than a negotiation with the entire codebase. The velocity arrives later, but it arrives and then it stays, instead of decaying as the system accumulates decisions nobody made deliberately.
Software that lasts a decade is not software that was built with more care in some general sense. It is software where the small number of genuinely irreversible decisions were made slowly, and everything else was made quickly.
