Budgets · the honest version
It's almost never the estimate that was wrong. Here's what actually happened - and how to prevent it.
// budgets
The statistics on software project failure are well-known and consistently grim: over budget, over schedule, and under-delivered at rates that would be unacceptable in almost any other industry.
What's less discussed is why - specifically, what actually happens between "we estimated X" and "we spent 2X." Understanding the causes is the first step to avoiding them.
// the short answer
Budget overruns almost always come from one of four sources.
Most over-budget projects aren't failed by bad estimates. They're failed by organizational and process problems that an estimate could never capture.
// the six causes
Each overrun usually traces to one of these, and each is preventable.
The most common cause, and the most preventable. Features and requirements get added after the estimate was set - without a corresponding adjustment to budget and timeline. It's rarely one dramatic change; it's accumulation. Individually each seems reasonable; cumulatively they add 30–50%.
Prevention: a clear, documented scope at the start, and a change-order process that makes additions visible with explicit budget and timeline implications.
The project was estimated without adequate discovery, so the real complexity revealed itself during development. Some genuinely can't be known without building - but much would have been visible with thorough upfront work.
Prevention: a discovery phase before development - two to four weeks to surface the complexity and estimate from knowledge, not assumptions.
Features built, then changed, then rebuilt are a compounding cost - the build, the change, the rebuild, and the downstream effects. It stems from unstable requirements or misunderstood ones.
Prevention: clear requirements documentation and stakeholder alignment before features are built - story reviews, design reviews, and explicit sign-off.
Technically straightforward projects run into decision bottlenecks, integration delays, internal dependencies, and environment/access issues that inflate cost without adding output.
Prevention: a kickoff that maps all external dependencies and their owners, clear escalation paths, and a PM who actively manages the critical path - not just the task list.
Moving fast by cutting corners - deferring testing, hardcoding config, skipping docs - feels like efficiency now and becomes expensive remediation later.
Prevention: engineering standards that don't flex under timeline pressure, and a culture where "we'll fix it later" gets challenged, because later is always more expensive.
Underprice testing and you pay for it at the end - or after launch, which is worse. Bugs found in testing are dramatically cheaper to fix than bugs found in production.
Prevention: test coverage is part of the estimate, not a line item to cut when the budget gets tight.
// before you start
A real estimate requires a real understanding of scope. Discovery work pays for itself.
Formally - not a Slack message or a verbal agreement. A written scope that stakeholders sign off on.
Every project will have changes. The process for managing them should exist before the first request, not after.
Know who you're depending on, what you need from them, and when you need it.
It's not optional. It's cheaper than the alternative.
A budget without contingency is a budget that will be exceeded. 15–20% is a reasonable baseline for projects of moderate complexity.
None of this is exotic. It's the discipline that keeps budgets honest ↓
// how we manage it
We don't start projects without discovery. We surface scope additions as cost decisions, not as free additions. We communicate budget impact proactively - before it has already happened.
It doesn't guarantee that projects come in exactly on budget - no honest person will promise that. But it means surprises happen rarely and early, rather than frequently and late.
ready when you are
Tell us what you're planning. We'll scope it with discovery, price it transparently, and flag budget impact before it happens - not after.