Skip to content
Richard Cooper
Go back

How much design up front?

There are two caricatures, and everyone has met both.

The first designs everything before anyone writes a line. Component diagrams, a data model down to column types, sequence diagrams for flows the business hasn’t confirmed it wants. Six weeks in there is a beautiful document and no software, and the first real requirement invalidates a third of it.

The second starts typing on day one. No diagram, no boundaries, a schema that grew rather than got designed. It’s genuinely faster for a month. Then the fourth feature needs the seam nobody put in, and you discover that the prototype has quietly become production.

The interesting thing isn’t that both fail. It’s that both are answering a question neither has bothered to ask properly: which decisions actually need designing before you build, and which ones are better answered by building?

This isn’t the same question as when to decide

Worth separating from a question I’ve written about before. The last responsible moment is about timing — you have identified a decision, and you’re working out how long you can responsibly leave it open.

This is the question upstream of that: for a given decision, is design even the right instrument? Some things you can reason your way to at a whiteboard. Others you cannot know until something is running, and no amount of additional design will conjure the missing information. Treating those two categories the same way is where both caricatures come from — one designs the unknowable, the other builds the knowable and hopes.

Design in proportion to the cost of being wrong

The first cut is the one I keep coming back to: how expensive is this to undo?

One-way and two-way doors is the whole heuristic. A choice you can reverse in an afternoon does not need a design review; it needs someone to make it and move on, because the cheapest way to evaluate it is to live with it for a week. A choice that will be load-bearing for a decade — the boundaries between services, who owns which data, the shape of a public contract, anything that ends up in someone else’s code — earns design attention out of all proportion to the effort of making it, because the effort of making it is not the cost.

The practical version: before designing anything, ask what it would take to change your mind later. If the answer is “an afternoon”, stop designing. If the answer is “a migration and a coordination meeting with three teams”, you are looking at the part that deserved the six weeks.

This is also why the up-front question can’t be answered globally. “How much design up front” has no number, because a project isn’t one decision — it’s dozens, at wildly different reversibility. The right answer is nearly always a lot for a few things and almost none for most of it, and the skill is telling which is which.

You cannot design your way to information you don’t have

Here’s the failure mode I find more interesting, because it wears the costume of rigour.

Analysis paralysis usually isn’t excessive caution. It’s a category error: trying to reason an answer that can only be measured. Will this query be fast enough at ten times the volume? Does that third-party API behave the way its documentation claims? Will this domain model survive contact with real, messy, historical data? You can hold a workshop about any of those. You will produce an opinion, and the opinion’s accuracy will be roughly what it was before the workshop.

The way out isn’t more design or less design. It’s noticing which kind of question you’re stuck on and buying the information instead — write the smallest thing that answers it. A spike against the real API for a day tells you more than a week of reading its documentation, because the thing you need to know is precisely the part the documentation gets wrong.

So the rule I’d actually offer: design the decisions you already have the information to make, and build something to buy the information for the rest. A team that has been arguing for three days is almost always arguing about something that could have been measured in one.

The spike’s failure mode is that it ships

And now the honest turn, because “just build a prototype and find out” has a failure mode of its own, and it isn’t sloppiness.

It’s that the spike works. It answers the question, and then — because it exists, and because there is pressure — it becomes the thing. Nobody re-decides. The exploratory code you wrote to learn whether an approach was viable is now load-bearing, carrying every shortcut you took precisely because you knew it was going in the bin.

The fix is cheap and almost nobody does it: decide before you start whether you are building the thing or buying information, and write that down. If it’s a spike, it’s disposable by contract — you have committed in advance to throwing it away and reimplementing with what you learned, which is optimising for deletion applied to your own working code. An ADR is a perfectly good place to record “we spiked X, learned Y, and the spike is not the implementation.”

If you can’t bring yourself to commit to deleting it, that’s useful information too: you weren’t spiking, you were building without deciding to.

Where this leaves the up-front design you do keep

This is the third post in an accidental thread. Modular microliths argued for starting with internal boundaries inside one deployable rather than distributing on day one. You can’t draw a boundary the business hasn’t decided argued that some boundaries aren’t yours to design at all — if the business hasn’t made the cut, designing harder just encodes an ambiguity invisibly.

Put those with the above and the up-front design worth doing gets quite small and quite specific: the seams. Where are the boundaries, who owns what data, what crosses between them, and which of those crossings are contracts other people will build on. That’s the part that’s expensive to change, and it’s the part you can mostly reason about without running anything.

Almost everything else — the internals behind a boundary, the shape of a class, which library, the schema of a table only one module reads — is a two-way door dressed up as a design decision. And since there is no final architecture to find anyway, time spent perfecting the reversible parts is time spent decorating something you’re going to change.

The honest limits

The one-line version

There’s no correct amount of design up front, because a project isn’t one decision. Design the things that are expensive to undo and that you actually have the information to decide; build something small to buy the information for the rest; and commit in advance to throwing that away, because the spike that quietly becomes the architecture is the real cost of “let’s just find out”.


Share this post:

Previous Post
A Design Authority without the bureaucracy
Next Post
Resilience patterns that earn their keep