Skip to content
Leadership Garden Leadership
Garden

Software Engineering Laws - Time & Estimation

7 min read
Series Software Engineering Laws Part 3 of 11
Software Engineering Laws - Time & Estimation
Table of Contents

Hofstadter’s Law

It always takes longer than you expect, even when you take into account Hofstadter’s Law

This isn’t a joke; it’s a fundamental truth about creative work. Software development is a process of discovery, not a predictable assembly line. You can’t perfectly estimate the time it will take to solve a problem you’ve never solved before.

Why it happens: The “unknown unknowns.” You can plan for the work you know about, but you can’t plan for the third-party API that has a critical bug, the cryptic legacy code you have to integrate with, or the security vulnerability discovered mid-sprint.

What to do about it: Stop treating estimates as promises. Use ranges instead of single numbers (“This is likely to take 3–5 weeks”). Your confidence in an estimate should be directly proportional to how much you know. An estimate for a task you’ve done ten times is reliable. An estimate for a novel R&D project is a guess. The goal is not to be right but to be less wrong over time. Embrace uncertainty and re-forecast often.


Parkinson’s Law

Work expands so as to fill the time available for its completion

This law explains the strange time-bending nature of software tasks. A feature given a two-week estimate will invariably take two weeks, filled with research, refactoring, and extensive testing. The same feature, given a hard one-week deadline, will often be completed in that week, stripped down to its essential components. The deadline itself, not the work, frequently defines the effort.

Why it happens:

  • Lack of Urgency: A generous deadline removes the pressure to find the most direct path. The work is padded with lower-priority activities, such as gold-plating (adding non-essential polish), exploring tangential technical curiosities, or prematurely optimizing code that isn’t a bottleneck.
  • Perfectionism as Procrastination: Without a forcing function, engineers can fall into the trap of seeking a “perfect” solution rather than an effective one. The extra time is consumed by over-engineering and handling hypothetical edge cases that provide diminishing returns.
  • The “Student Syndrome”: Humans tend to start work at the last possible moment. A two-week task often sees little progress in the first week, as the perceived deadline is still far away. The real work then begins under pressure in week two.

What to do about it:

  1. Use Timeboxing and Aggressive Scoping: Instead of asking, “How long will this take?”, frame the question as, “What is the simplest version of this we can ship in three days?” Fix the time, and be ruthless about adjusting the scope to fit. This forces prioritization and focuses the team on delivering value quickly.
  2. Break Down Work into Smaller Quanta: Parkinson’s Law thrives on large, amorphous tasks. A three-month project will expand to fill three months. By breaking it into a series of one-week deliverables, you create multiple, smaller deadlines. This maintains a constant sense of manageable urgency and prevents work from drifting.
  3. Create Artificial Deadlines for R&D: For tasks without a hard external deadline (e.g., technical debt reduction, research spikes), impose one. Give the team a fixed budget of time (e.g., “Take two days to investigate a new library and report back”). This prevents open-ended research from becoming a time sink and forces a concrete outcome.

Ninety-Ninety Rule

The first 90 percent of the code accounts for the first 90 percent of the development time. The remaining 10 percent accounts for the other 90 percent

This rule captures the painful chasm between “it works on my machine” and “it’s running reliably in production.” The initial feature development—the “happy path” where everything goes as expected—is often the fastest and most gratifying part. The final, brutal stretch involves the “invisible work” required to make software robust, secure, and maintainable. This last 10% routinely demolishes timelines and destroys morale.

Why it happens:

  • Exponential Edge Cases: The happy path is one linear flow. The unhappy paths (network failures, invalid inputs, race conditions, downstream service errors) are numerous and complex. Thoroughly handling these edge cases can require more code and testing than the core feature itself.
  • Integration Hell: Code that works perfectly in isolation often breaks when combined with other systems. Unforeseen conflicts, mismatched data contracts, and subtle environmental differences between staging and production create a minefield of bugs that only surface at the very end.
  • The “Production-Ready” Tax: A feature isn’t done until it has proper logging, monitoring, alerting, documentation, and security hardening. This work is non-negotiable but often mentally discounted or forgotten during initial estimation, leading to a massive, unplanned effort at the end of the cycle.

What to do about it:

  1. Expand Your “Definition of Done”: “Done” is not when the feature is coded. “Done” must mean it is tested, documented, monitored, and successfully deployed. Create a checklist for what “done” entails for every piece of work. This forces the “last 10%” to be considered from the beginning, not as an afterthought.
  2. Integrate Continuously and Deploy Early: The longer you wait to merge and deploy code, the more painful the integration will be. Practice continuous integration to surface conflicts in small, manageable increments. Deploying to a staging environment early in the development process reveals the “production-ready” gaps when they are still cheap to fix.
  3. Make the Invisible Work Visible: Do not bury the “production tax” in your estimates. Explicitly create sub-tasks for “Add logging,” “Create dashboard,” “Write integration tests,” and “Update deployment pipeline.” This makes the true cost of the feature visible to all stakeholders and ensures time is budgeted for it, rather than being a surprise.

Cohn’s Laws of Estimates

Estimates are Waste, Non-Transferable, Wrong, Temporary, and Necessary

Estimation is the great paradox of software development. It’s a practice that is simultaneously flawed at a fundamental level and absolutely essential for coordinating any non-trivial effort. Engineers resent the time spent on it and the false precision it implies, while the business cannot function without some way to forecast and plan. Mastering this paradox requires accepting its constituent truths.

Why it happens:

  • Waste: The act of estimation produces zero direct value for the end-user. It is pure internal overhead—time spent debating points or days is time not spent writing code.
  • Non-Transferable: An estimate is an expression of one person’s (or team’s) understanding of a problem, filtered through their specific skills and experience. The estimate made by a senior engineer cannot be handed to a junior engineer with the expectation that it will hold.
  • Wrong: Because software development is a process of discovery, not manufacturing, an estimate is a prediction about an unknown future. Unforeseen technical hurdles, changing requirements, and hidden complexities guarantee that nearly every estimate will be incorrect in retrospect.
  • Temporary: An estimate is a snapshot based on the knowledge available at a single point in time. The moment work begins and the first line of code is written, new information is discovered that instantly makes the original estimate obsolete.
  • Necessary: Despite all these flaws, we must estimate. We need to decide if a project is viable, coordinate dependencies between teams, create roadmaps, and provide stakeholders with a general idea of when to expect new functionality. Anarchy is the only alternative.

What to do about it:

  1. Use Ranges, Not Single Numbers: Stop giving single-point estimates. Instead of “five days,” say “this is likely a 4- to 10-day task.” This communicates uncertainty honestly and conditions stakeholders to think in terms of probability, not promises. The wider the range, the higher the uncertainty.
  2. Focus on Relative Sizing: Instead of estimating in hours or days, use abstract story points or t-shirt sizes (S, M, L, XL). The goal is not to predict the exact duration, but to quickly determine if Task A is roughly twice the effort of Task B. This is faster (less waste) and more accurate for near-term planning.
  3. Estimate Collaboratively and Just-in-Time: The people doing the work must be the ones estimating it (respecting the “Non-Transferable” law). Use techniques like planning poker to get a quick consensus. Crucially, only estimate work for the immediate future (e.g., the next sprint or two). Detailed long-range estimates are almost pure fiction.
  4. Re-forecast Frequently: Treat your roadmap as a living document, not a stone tablet. Continuously update your forecasts based on the team’s actual, measured velocity and new information learned. This embraces the “Temporary” nature of estimates and builds trust through transparency, rather than by defending a known-wrong plan.
Share

Series

Software Engineering Laws

A practical sequence on the recurring laws and constraints that shape engineering work, from coding and architecture to testing and performance.

Open series page

Explore further

Keep going with a few related posts, then branch into the topic hubs and collections around the same ideas.

Continue with these