Skip to content

There Is No Perfect Software

Every piece of software contains tradeoffs. Every running system has known defects, known limitations, and unknown ways of failing that nobody has discovered yet. This is not a temporary state to be fixed. It is the permanent condition of running software, and pretending otherwise is one of the most reliable ways to make decisions that produce worse software.

Acknowledging that perfection is impossible is not a counsel of low standards. It is the precondition for serious standards. A team that believes software can be made defect-free invests in catching defects that do not matter while missing the ones that do. A team that accepts the impossibility of perfection invests in the things that actually determine whether the software serves its users well: reliability, observability, maintainability, and the ability to recover when something goes wrong.1

Why Perfection Is Not the Goal

The argument against perfection-as-target is partly economic and partly empirical:

  • Each additional unit of quality costs more than the last. Defect rates can be driven down, but not at a constant cost. Eliminating the first ninety percent of defects is much cheaper than eliminating the next nine, which is much cheaper than the next ninety-nine hundredths, and so on. At some point the team is spending more on prevention than the prevention can possibly recover.
  • Software is too complex to fully verify. The set of possible inputs, states, and interactions in any non-trivial system is larger than any test suite can enumerate. Formal verification works at small scale; at production scale, most teams cannot afford it for most code.26
  • The environment keeps changing. Even code that was correct at the time it was written can become incorrect as dependencies update, traffic patterns shift, business rules change, and new threats emerge. "Perfect" software does not stay perfect.
  • Most of what fails was not anticipated. The defects that produce the worst incidents are usually the ones nobody thought to test for. Investing in catching the imagined defects does not catch them.

A goal of perfection misallocates effort. A goal of appropriate quality, defined for each system based on what failure costs, lets the team invest where the investment matters.

What Mature Systems Actually Look Like

A team that has accepted the impossibility of perfection invests in a different set of properties. Mature systems are not defect-free. They are:

  • Observed. The team can see what the system is doing in production. When something is wrong, they can find it.3
  • Recoverable. When something goes wrong, the team can restore service quickly. Recovery capability matters more than failure prevention beyond a certain point.4
  • Maintainable. The system can be changed safely, by people who did not write it, without an unreasonable amount of effort. The team can fix problems and add capabilities without breaking what works.5
  • Sized to the team that owns them. The operational complexity matches the resources available to operate it. A system that requires three full-time engineers to operate but has one engineer assigned to it will degrade reliably.
  • Continuously updated. Security patches, dependency upgrades, regulatory changes, and small improvements happen as a normal part of the work. A system that nobody is currently updating is a system that is silently accumulating risk.

These are operational properties, not quality targets. They are what allows a team to serve users well in a world where the underlying systems are imperfect and will remain so.

Ongoing Work Is Not Failure

A persistent misunderstanding in software is that maintenance, security updates, and operational response are signs that the original work was incomplete. They are not. They are the normal life of a running system.

A few of the costs that are easy to misread as failure:

  • Monitoring and alerting. Continuous observation of a working system. Not evidence of fragility; evidence of operational maturity.
  • Support and triage. Ongoing customer questions, edge cases, and incidents. Some are bugs; many are simply what running a product in the world looks like.
  • Dependency upgrades. Libraries, frameworks, and platforms move forward. The team that does not move with them eventually has to do a much larger upgrade later, at much higher cost.
  • Security patching. New vulnerabilities are discovered in software the team did not write. Responding to them is part of operating responsibly, not a sign that the original choice was wrong.
  • Refactoring and small improvements. Code that is being changed routinely is code the team can keep healthy. Code that is never touched is code that accumulates problems silently.

A budget or contract that does not account for this work has set up the team for failure. The work happens whether or not it was budgeted; the only question is whether the team is funded to do it well.

What This Looks Like in Practice

A few habits follow from accepting that software is permanently imperfect:

  • Set explicit reliability targets, not perfection goals. What level of uptime, error rate, and response time does this system need? Different systems need different targets, and being explicit forces the right conversation.
  • Invest in detection and recovery. A team that can find and fix problems quickly produces a more reliable customer experience than a team that almost never has problems but cannot recover when they do.
  • Budget for ongoing operational work as a permanent line item. Maintenance is not the cost of failure; it is the cost of having software. Plan for it.
  • Treat incidents as information, not as failures of will. The system told the team something. The right response is to learn from it, not to promise it will never happen again.
  • Resist contracts and incentives that promise perfection. Anyone who promises defect-free software either does not know what they are promising or intends not to deliver it.

Operational reality

A responsible team reduces risk. It does not promise magic. The discipline of building reliable software starts with the honest acknowledgment that perfection is not on the menu.

See also: Perfection Is Irrational, Quality Is Designed In, Testing Is Not Quality, Observability, Incident Response, Maintainability.



  1. See Perfection Is Irrational for the longer treatment of the cost curve and the failure modes of both under- and over-engineering. 

  2. Edsger W. Dijkstra, "Notes on Structured Programming" (1970). The originating articulation of the limit of testing as a verification strategy: "Program testing can be used very effectively to show the presence of bugs but never to show their absence." The argument applies more broadly to verification of any non-trivial software system. 

  3. See Observability

  4. See Incident Response

  5. See Maintainability

  6. Frederick P. Brooks Jr., No Silver Bullet: Essence and Accident in Software Engineering (1986), reprinted in the anniversary edition of The Mythical Man-Month. Brooks's distinction between essential complexity (the genuine intricacy of the problem domain) and accidental complexity (mismatch between problem and tools) reframes the perfection question: accidental complexity can be reduced, but essential complexity cannot be engineered away. Perfect verification is unreachable not because of engineering immaturity but because of the intrinsic nature of the problems software solves.