Skip to content

· 2 min read

Decide before you build: the user, the constraint, the refusal

Most product failures are not engineering failures. They are failures to decide three things early enough to matter.

  • Product
  • Strategy

When a product goes wrong, the post-mortem usually blames execution: the team was slow, the estimate was off, the architecture did not scale. Sometimes that is true. More often, the build was fine and the decisions underneath it were never made.

There are three decisions I try to force before anyone writes production code.

1. Name the user

Not "users". One user, specific enough to disagree about.

"Small businesses" is not a user. "An operations lead at a company of twenty people who currently tracks this in a shared spreadsheet and loses a day a month reconciling it" is a user. You can check whether a design serves that person. You cannot check whether it serves "small businesses", which is why every feature request can claim to.

Naming one user does not mean ignoring everyone else. It means that when two requests conflict, there is a tiebreaker that is not whoever argued loudest.

2. Name the constraint

Every product has one thing that, if you get it wrong, nothing else matters. It is rarely the thing on the feature list.

For a marketplace it is usually liquidity — enough supply that buyers find something, enough buyers that suppliers stay. For an inspection system it is traceability — a record that holds up when someone challenges it later. For a real-time game it is agreement — every player seeing the same thing happen at close enough to the same time.

Once the constraint is named, the architecture has a job. Without it, the architecture optimises for whatever the lead engineer found interesting last year.

3. Name the refusal

This is the one teams skip, and it is the most valuable.

A refusal is a thing the system will deliberately not do, written down, with the reason. "We will not support offline editing, because conflict resolution would dominate the roadmap and our users are always online." "We will not let creators set arbitrary licence terms, because buyers need to understand a licence at a glance."

Refusals do three jobs:

  • They protect the constraint. Most scope creep is a slow erosion of the one thing that mattered.
  • They make estimates honest. A system that might do anything cannot be estimated.
  • They end arguments. The debate happened once, it was recorded, and it does not need to happen every sprint.

A refusal is not permanent. It is a decision with a reason, and when the reason stops being true, you revisit it. What it is not is an accident — a capability missing because nobody thought about it.

The test

If you cannot write the user, the constraint and the refusal on an index card, the project is not ready to build. It might be ready to prototype, to research, to talk to customers. Those are all useful. But writing production code before those three exist is paying the most expensive rate for the cheapest kind of learning.

I spend the expensive part of a project here, deliberately. It is the part that makes the rest cheap.