Maintainability, Simplicity & Cost
Introduction
For this entire section you've been chasing qualities you could name: make it fast, make it available, make it scale, make it consistent. Those get written into specs, argued about in meetings, and celebrated when they ship. This last lesson is about three qualities that almost never make it into the spec — and that quietly end up costing more than all the rest combined:
Maintainability, simplicity, and cost — the requirements nobody states, and everybody pays.
Here's the fact that reframes how you should think about every system you build: the majority of a system's total cost is not building it — it's owning it. Think of a house. The purchase price is the part everyone talks about, but over thirty years the mortgage interest, the repairs, the taxes, and the utilities dwarf it. The sticker price is the tip of the iceberg; the real cost is the ninety percent underwater. Software is exactly the same — and for software, that submerged mass is maintenance: the endless work of understanding the code, changing it, operating it, and fixing it, year after year.
So the qualities that decide how expensive that maintenance is — how maintainable, how simple, how costly to own your system is — are not soft, optional nice-to-haves. They're where most of the money and time go. By the end of this lesson you'll understand why maintenance dominates, what actually makes a system maintainable, the two opposite ways teams blow the budget, and the single idea that ties this whole section together. Let's go under the waterline.

Owning Costs More Than Building
The most expensive line in a system's life isn't the servers, and it isn't the initial build. It's engineers' time — spent, over years, keeping the thing alive and changing it. This is the concept of total cost of ownership (TCO): the true price of a system is everything you'll pay across its whole life, not just what it costs to stand it up.
Two things make this counterintuitive, and both matter:
- Maintenance dwarfs the build. A system is written once and then lived in for years — every bug fix, every new feature, every migration, every 3 a.m. page. That ongoing work typically costs far more, in total, than the original build. The cheap-to-build option and the cheap-to-own option are often not the same option.
- People cost more than machines. It's tempting to optimize for hardware — squeeze the servers, shave the cloud bill. But an engineer's salary dwarfs the cost of the machines they work on. A design that wastes a little compute but saves your team hours every week is usually the cheaper design, period. Time is the scarce resource; hardware is comparatively cheap.
This flips a lot of instincts. The "efficient" hand-tuned system that only one wizard understands is expensive — because every change routes through that wizard, and onboarding takes months. The slightly-less-clever system that any new hire can read and safely change is cheap — because the expensive resource, human attention, flows freely. Optimize for the thing that actually dominates the bill: the cost of understanding and changing the system over time. That's what maintainability is.
Maintainability: Operable, Simple, Evolvable
"Maintainable" sounds fuzzy, so here's a precise, useful breakdown — the three properties Martin Kleppmann uses in Designing Data-Intensive Applications, and they're worth memorizing because they give you a checklist for the underwater 90%:
- Operability — is it easy to run? Can the operations team keep it healthy without heroics? That means good monitoring and visibility into what it's doing, automation for routine tasks, no reliance on one irreplaceable machine (or one irreplaceable person), clear documentation, sane defaults, and predictable behavior. A system you can't see into or automate is one you'll be hand-nursing forever.
- Simplicity — is it easy to understand? Can a new engineer read a part of the system and grasp what it does without a two-week archaeology dig? Simplicity is the deliberate removal of complexity so the system stays legible. This is the big one, and the next section is all about it.
- Evolvability — is it easy to change? Requirements always change — new features, new scale, new rules. An evolvable system bends to accommodate them; a rigid one shatters, and every change becomes a risky, weeks-long ordeal. Since change is the one guarantee in software, designing for it is designing for reality.
Notice the through-line: all three are about the cost of the future. Operability is the cost of running it tomorrow; evolvability is the cost of changing it next quarter; simplicity is the cost of understanding it every single day in between. Maintainability is just a name for keeping those future costs low — and the enemy of all three has one name.

Simplicity: The War on Accidental Complexity
The enemy is complexity, and the single most useful idea for fighting it comes from Fred Brooks' classic essay No Silver Bullet. He split complexity into two fundamentally different kinds:
- Essential complexity is inherent to the problem. If your product genuinely needs to do thirty different things, those thirty things are essential — no amount of cleverness makes them go away. Payments really are complicated; healthcare rules really are tangled. You can't wish essential complexity out of existence.
- Accidental complexity is the mess we add on top — with clumsy tools, hasty shortcuts, needless abstractions, five ways to do the same thing, and a taste for cleverness. It has nothing to do with the problem; it's self-inflicted. And unlike the essential kind, you can remove it — that's what high-level languages did to the accidental complexity of writing assembly.
The entire discipline of simplicity is the relentless removal of accidental complexity — keeping the essential, cutting everything else. And it matters enormously because complexity doesn't add up politely, it compounds. As a system grows, the number of interactions between its parts grows much faster than the parts themselves — every new piece can touch every old one. That's why a system that felt fine at ten thousand lines becomes a terrifying "big ball of mud" at a hundred thousand: the complexity grew nonlinearly, and it dragged down comprehension, velocity, and reliability all at once.
One hard truth to make peace with: simplicity is not easy. It takes real effort and taste to make something simple — far more than it takes to leave it complex. As the old line goes, "I would have written a shorter letter, but I didn't have the time." Simple is the expensive-to-produce, cheap-to-own choice. Which is exactly the trade you're about to feel.
See It: Ship It Cheap, Then Pay the Bill
The cost of complexity is abstract until it's a number climbing in front of you. Below, you run a product for a few years and ship features — each one two possible ways:
- ⚡ Ship FAST — take the shortcut. It's cheap right now, but it piles on tech debt.
- ✓ Ship CLEAN — do it properly. It costs a bit more now, but it adds no debt.
Then watch the catch that defines this whole lesson. Every unit of debt charges interest: once debt is high, the next feature — and even just keeping the lights on — costs multiples more than it should. Ship FAST a few times and the early features look like a bargain, but the debt spirals, your cost per change balloons, and the maintenance bar swallows the build bar whole. You can 🧹 Refactor — pay a chunk now to knock the debt down (pay off principal) — and watch your velocity recover.
Try to get the lowest total cost over the years. You'll discover the thing every experienced engineer has learned the hard way: the cheapest system to build is almost never the cheapest system to own.

Technical Debt: A Loan You Pay Off Forever
The interest you just watched compound has a name: technical debt, and the financial metaphor is exact enough to reason with. When you take a shortcut to ship faster — the quick hack, the copy-paste, the "we'll fix it later" — you are borrowing. You get speed now, and in exchange you agree to pay interest on every future change: because the code is messier, every task that touches it takes a little longer, forever, until you refactor to pay down the principal.
The crucial insight is that debt, like money, is not inherently bad — it's a tool. Taking on debt to hit a critical launch, validate an idea before over-investing, or seize a market window can be exactly right (that's just YAGNI and premature-optimization wisdom in reverse — don't polish what might get thrown away). A startup racing to product-market fit should carry some debt. The danger isn't debt; it's unmanaged, compounding debt: shortcut after shortcut with no repayment, until the team spends all its time servicing interest — fighting the mess — and has none left to build. That's bankruptcy, and it's how healthy products slowly grind to a halt.
So the discipline is not "never take debt." It's: take debt deliberately, track it, and pay it down before the interest eats you. Know when you're borrowing, know why, and schedule the refactor before the velocity graph turns down. "We'll clean it up later" is where debt goes to compound in the dark — because later has a way of never arriving on its own.
The Opposite Sin: Over-Engineering
If tech debt is doing too little, there's an equal and opposite way to blow the budget: doing too much. Over-engineering is building elaborate machinery for a future that never arrives — the configurable-everything framework for the one use case you have, the microservices for your ten users, the shiny new database for a workload a boring one would handle fine. It feels responsible, like you're "future-proofing." It's usually the same mistake as premature scaling from earlier in this section: premature complexity, paid for in build time now and maintenance forever, for needs that mostly never come.
Two old principles are the antidote, and they're old because they're right:
- YAGNI — "You Aren't Gonna Need It." Don't build for hypothetical futures. Build what the problem in front of you actually requires. The flexibility you add "just in case" is complexity you pay for with certainty, for a benefit that's only a maybe.
- KISS — "Keep It Simple." The simplest thing that solves the real problem is almost always the right thing. Simpler solutions are easier to understand, test, operate, and change — cheaper to own on every axis.
And a close cousin for technology choices: choose boring technology. Every novel, shiny tool you adopt spends a scarce "innovation token" — a new learning curve, unknown failure modes, a thin ecosystem, one more thing nobody on the team fully understands. You only get a few of those tokens, so spend them where they truly create an edge, and let everything else be boring, proven, and dull — because boring technology is maintainable technology.
So there are two ditches on either side of the road: the hack (debt) and the gold-plate (over-engineering). The target between them is narrow and clear: solve the essential complexity, cleanly, and not one ounce more.
The Trade-off
Now zoom all the way out, because this lesson is the keystone of the whole section. Everything you've learned to want — high availability, low latency, big scale, strong consistency, and now maintainability and simplicity — has one thing in common:
Every non-functional requirement costs something. And you cannot maximize all of them at once.
More availability means more redundancy and more machinery to operate. More performance means more tuning and often more complexity. More scale means coordination and distributed-systems tax. Stronger consistency means latency and reduced availability. More of any quality is bought with the same three currencies — money, complexity, and engineering time — and a dollar or an hour spent on one is a dollar or an hour not spent on another.
| Push for more… | You pay in… |
|---|---|
| availability / redundancy | money + operational complexity |
| performance | tuning time + (often) complexity |
| scale | coordination + distributed-systems tax |
| strong consistency | latency + availability |
| features / flexibility | simplicity + maintenance forever |
So the job of an architect is not to maximize every quality — that's not even possible, and chasing it produces an over-engineered, unmaintainable, bankrupting mess. The job is triage: look at what this specific system actually needs, decide which two or three qualities genuinely matter, spend your budget on those, and consciously accept less on the rest. A bank spends on consistency and availability and gladly runs boring, simple tech. A viral photo app spends on scale and eventual consistency and doesn't care about strict ordering. Neither maxed everything. Both chose.
That choosing — matching the qualities to the real needs and the real budget — is the single most important skill this whole section was building toward. And it's exactly what you're about to practice.
Mental-Model Corrections
"Maintainability and cost aren't real requirements — just ship the features." They're the requirements nobody writes and everybody pays. Maintenance is the majority of a system's lifetime cost.
"Simplicity means fewer features / doing less." No — it's essential vs accidental complexity. A feature-rich system can be simple (well-abstracted); a tiny one can be a mess. You cut self-inflicted complexity, not scope.
"The cheap option is whatever costs least to build." No — total cost of ownership. The cheap-to-build hack is usually the expensive-to-own choice, and engineers' time dwarfs hardware.
"Use the newest, best technology." Boring, proven tech is more maintainable. Every shiny tool spends a scarce innovation token (risk + accidental complexity). Choose boring on purpose.
"Future-proof it now — more engineering is safer." Over-engineering wastes time and adds maintenance for needs that never arrive. That's premature complexity — the cousin of premature scaling.
"We'll clean up the tech debt later." Debt compounds, and later rarely comes on its own. Take debt deliberately and schedule the repayment.
"You can have all the NFRs." No — every NFR costs money, complexity, and time. You allocate a budget; good design is triage, not maximization.
Try It Yourself: Read the Underwater Bill
Fifteen minutes and you'll never look at an architecture the same way. Predict first: for a system you know, roughly what fraction of its total lifetime cost do you think went to the initial build versus everything since?
- Find the accidental complexity. Pick a piece of a codebase you work in that everyone dreads touching. Ask: how much of this difficulty is essential (the problem is genuinely hard) and how much is accidental (we made it hard — tangled dependencies, five ways to do one thing, a clever abstraction nobody understands)? The accidental part is pure, removable cost you're paying every day.
- Name the debt and the interest. List two or three shortcuts your system has taken. For each, estimate the interest: how much slower does that shortcut make a typical change today? Now ask the honest question — is anyone actually planning to pay it down, or is it just quietly compounding?
- Do the NFR triage. For something you're designing or maintaining, write down the NFRs (availability, latency, scale, consistency, simplicity, cost) and force yourself to pick the two or three that genuinely matter most — and, harder, name the ones you're willing to sacrifice. If you can't name what you're giving up, you haven't actually made a design decision.
That last exercise — ranking the qualities and owning the trade-offs — is precisely the muscle the section checkpoint is about to work. Do it once on something real and the checkpoint will feel like second nature.
Recap & What’s Next
The requirements you don't see until the bill arrives:
- Most of a system's cost is owning it, not building it — the maintenance iceberg — and the dominant cost is engineering time, not hardware.
- Maintainability = operability (easy to run) + simplicity (easy to understand) + evolvability (easy to change).
- Simplicity is the war on accidental complexity (self-inflicted, removable) while keeping the essential (inherent) — and complexity compounds as a system grows.
- Tech debt is a loan (fast now, interest on every change) — fine in moderation, bankrupting if unmanaged; over-engineering is the opposite sin — answered by YAGNI, KISS, and boring technology.
- The keystone: every NFR costs money, complexity, and time — you can't max them all. Good design is triage, not maximization: choose the qualities that matter for this system and consciously accept less on the rest.
And that completes the section. You started not knowing what "system design" even was; you now carry a working vocabulary for the whole discipline — scalability and statelessness, availability and reliability, single points of failure and redundancy, latency and throughput, performance and scalability, consistency, and the hidden trio of maintainability, simplicity, and cost. Every one of them is a lever, and every lever has a price. The only thing left is to prove you can use them together — to look at a real product and decide which qualities it actually needs. That's the Checkpoint: Core Concepts & Non-Functional Requirements — your first real taste of thinking like an architect.