How good decisions, made in good faith, drive software projects into terminal complexity

Nearly every week I watch someone make the best decision they can with the information in front of them. They aren't careless and they aren't malicious. They just can't see the part of the problem that matters most. After more than ten years of building software, I've come to believe that most failing projects aren't killed by one bad call. They're killed by a series of reasonable ones.

The adapter in the middle

A company is a stack of abstractions. At one end are the people who decide what gets built. At the other are the people who build it. In between is middle management.

I think of middle management the way I think of the adapter pattern in software engineering. An adapter takes two interfaces that were never designed to work together and translates between them, so that each side gets what it expects. When it works, neither side has to understand the other. That's also the problem: the two sides often never do.

Assume everyone is acting in good faith. The engineers are trying to improve the system they work in: to make the application stable, maintainable and able to last. The executives are also trying to improve the system they work in: to make the business solvent, competitive and able to last. Both goals are legitimate. So what happens when a business decision and the health of the software pull in opposite directions?

What usually happens is that the business decision wins. It isn't because anyone chose badly. It's because the cost of the decision doesn't make it through the adapter. Budgets and deadlines translate well across the layers. Complexity doesn't translate at all.

The Quality Triangle

To explain why, I need a model most project people already know: the triangle of scope, cost and time.

Depicts a triangle where at each respective corner is scope, cost, and time. Scope is labeled with What we build, cost is labeled with what we invest, and time is tabled with when we deliver. The center of the triangle is labeled quality with a yellow glow radiating from the center. At the absolute center is a black circle representing terminal complexity. The distance between the terminal complexity and the exterior lines is referred to the safe margin in the image.
The Quality Triangle

Every project balances three things: what we build (scope), what we invest (cost), and when we deliver (time). Quality isn't a fourth corner. It's the space inside the triangle, and that space is what the three corners protect.

I think of that space as a safe margin. It's the room a team has to make changes without breaking things. When the corners are in balance, the margin is wide. You can add a feature, refactor a module or fix a bug, and the system absorbs it.

At the center of the triangle sits the thing that margin protects you from: complexity.

Terminal complexity

Every project gets more complex over time. That's normal. Teams handle it by paying complexity down as they go: refactoring, testing, documenting, and taking the time to do things properly.

The trouble starts when complexity builds up faster than the team can pay it down. Each shortcut eats into the margin a little more. Eventually you reach what I call terminal complexity: the point where no change is safe anymore. Every fix breaks something somewhere else, often in a part of the codebase that seems completely unrelated. The system still runs, but it can no longer really be changed.

The Icarus Trap

So how does a project get there? Almost never all at once.

It happens like this. The business is short on money but still needs the system built or changed, so the budget shrinks. Scope stays the same, and so does the deadline. Something has to give. Nobody writes down what gave, but it was the margin. One edge of the triangle bends toward the center.

A quarter later, a big customer needs one more feature before renewal, so scope grows. Then a competitor ships early and the deadline moves up. Each decision pulls another edge inward. Each one is defensible on its own terms, and each one looks like progress from the top: more delivered, less spent, faster.

That's why I call it the Icarus Trap. Icarus didn't fall because he was reckless in one moment. He fell because flying higher felt like success the whole way up, and the wax melted gradually, out of sight, until it failed all at once. In a project, the wax is the margin. By the time anyone above the adapter can see it melting, an edge has already reached the core.

The warnings that don't get through

The people closest to the code almost always see it coming. Concern grows on the team, and people raise it, sometimes in meetings and sometimes in private. The warnings usually sound like this:

  • "Every fix breaks something else." Bug fixes cause new bugs, often in unrelated places.
  • "Nobody wants to touch that." Parts of the codebase become off limits.
  • "It's a small change, but it'll take two weeks." Estimates keep growing for work that hasn't grown.
  • "Only one person understands it." Knowledge collects in a single person, and that person becomes a bottleneck.
  • "We'll clean it up after the release." You've heard that before, and the cleanup never happened.

The warnings are raised, but they arrive through the adapter translated into something smaller. "The codebase is becoming unmaintainable" turns into "engineering wants more time," and wanting more time is exactly what the budget can't afford. Complexity has no line item, so the concern gets filed next to every other request for more.

Staying out of the trap

The way out isn't to stop making trade-offs. Every real project makes them. The way out is to make them in the open.

  1. Make every trade explicit. If the budget drops, say out loud which corner moves to make up for it: scope or time. If the answer is "neither," then the answer is really "quality," and that should be a conscious decision rather than a default.
  2. Give the margin a number. Track something that shows complexity to people who never read code. That could be the rate of regressions, how long small changes take, or how often changes fail. A trend on a chart gets through the adapter better than a feeling in a meeting.
  3. Budget to pay complexity down. Set aside part of every cycle for maintenance work, the way you'd budget for any other ongoing cost.
  4. Treat escalating warnings as data. When several engineers independently raise the same worry, that's not complaining. It's the only instrument you have that can see the core.

And if you're already there? Getting out of terminal complexity is expensive. It usually means cutting scope hard, a long period of stabilization, or a rewrite. The only cheap way to handle the Icarus Trap is not to fly into it.

In Closing

None of the people in this story are villains. The executive cutting the budget is protecting the business. The manager in the middle is translating as faithfully as they can. The engineer raising concerns is protecting the system. The trap forms in the gaps between them, where a decision's cost can't be seen by the person making it.

Balance the corners. Protect the margin. And when the people closest to the work say the wax is melting, believe them.