Projects that go badly rarely go badly during the build. They go badly in the week before it, when nobody writes down what "done" means.
I've shipped enough client work to know that the estimate is the least interesting part of a quote. The interesting part is whether both of us are describing the same product. Here's the process I run before I give anyone a number.
The brief is the real deliverable
Before any number exists, I write a one-page brief and send it back. Not a proposal with logos and stock photos — a plain page that says what the thing does, who uses it, what it explicitly does not do, and what has to be true for it to be considered finished.
If you read that page and say "yes, that's it," we've removed most of the risk from the project. If you read it and say "wait, no," we just saved four weeks. Either outcome is worth a day of writing.
That day costs the same whether the project is a landing page or a platform, so it happens before any pricing conversation, on every kind of work I take on.
Five questions before any number
- 01
Who is the first real user, by name?
"Small businesses" is not an answer. If you can't name one person who will use this in week one, the product isn't scoped, it's imagined.
- 02
What already exists?
A design file, a spreadsheet people already use, an old app to migrate off. Existing artefacts cut estimates more than anything else.
- 03
What is the deadline attached to?
A funding round, a trade show, a contract date — or nothing. A date with a reason behind it changes what I'd cut. A date with nothing behind it is just anxiety.
- 04
Who decides?
One person with final say makes a project roughly twice as fast as a committee. This is the single best predictor I've found of whether a build goes well.
- 05
What happens after launch?
If nobody owns the thing in month two, I'd build it differently — fewer moving parts, more boring choices, no infrastructure that needs babysitting.
Why I quote a range
A single number pretends to a precision nobody has. I quote a range with the assumptions printed next to it, and the range narrows as those assumptions get resolved.
Every range is calibrated against builds I've already shipped, not against how long the work would take if nothing went wrong.
What widens the range
- Third-party integrations whose docs I haven't read yet
- Anything described as "just like Stripe, but for us"
- Design that doesn't exist yet, especially on mobile
- Data migration from a system I can't see
What narrows it
- Finished designs, or permission to use my own judgement
- A named decision-maker who replies within a day
- A written cut line (below)
An estimate is not a promise about the future. It's a statement about how much I currently understand.
Draw the cut line early
Every scope gets split into two lists: above the line, which ships, and below the line, which is real work we've agreed to defer. Writing the second list is the important half. It means that when the timeline gets tight — and it will — we're choosing from a list we already agreed on instead of negotiating under pressure.
Above the line
Ships
Below the line
Real work we've agreed to defer
The technical version of the same discipline is refusing complexity you haven't earned yet. I wrote about the specific version of this on the framework side in App Router mistakes, and most of my notes on shipping circle back to the same idea.

