(Thoughts after watching “Clean code is bad. What makes code maintainable?”)
For a long time, I thought I was doing something wrong.
Every time I struggled to debug a codebase full of tiny functions, deep abstractions, interfaces, and “beautiful” patterns, I blamed myself.
- Maybe I’m not experienced enough.
- Maybe I don’t understand Clean Code deeply enough.
Then I watched “Clean code is bad. What makes code maintainable?” by Internet of Bugs, and for the first time in years, something clicked.
This wasn’t about hating Clean Code for the sake of it. It was about something far more important:
Maintainability is not about how elegant code looks.
It’s about how fast you can understand, debug, and fix it when things go wrong.
And things always go wrong.
The Big Lie: “Clean Code Prevents Bugs”
One of the most dangerous assumptions behind Clean Code culture is the idea that if you follow the rules strictly enough, bugs somehow stop happening. But, They don’t.
- Bugs are inevitable.
- Requirements change.
- Edge cases appear.
- Production behaves differently than your local machine.
So the real question is not “How do I avoid bugs?” It’s:
“When a bug happens at 2 AM, do I know exactly where to look?”
That’s what maintainability actually means.
Debuggability = Maintainability
When I’m debugging, I don’t read code like a novel. I search for:
- a string in a log
- a value that looks wrong
- a condition that shouldn’t have triggered
From there, I work upwards, following the logic.
But Clean Code often breaks this workflow:
- Logic is split across 10 tiny methods
- Each method lives in a different file
- Polymorphism hides the real execution path
- Important decisions are buried inside “private helpers”
Now instead of fixing a bug, I’m playing Whack-A-Mole:
Fix something here → something breaks over there → repeat.
That’s not maintainable. That’s fragile.
Locality of Behavior Matters (A Lot)
If a piece of logic changes, I want:
- the code that uses it
- the code that decides it
- the code that mutates state
Clean Code often pushes logic away from where it matters, just to satisfy rules like:
- “Functions must do one thing”
- “Functions should be tiny”
- “Always extract helpers”
Extraction is not free.
Every new abstraction adds cognitive cost.
Abstractions Are Not Evil — Premature Abstractions Are
This isn’t an anti-abstraction rant. Abstractions are powerful when the domain is well understood.
But too often, we abstract:
- before requirements stabilize
- before edge cases are known
- before real usage exists
The result?
- generic helpers no one understands
- APIs that hide important context
- code that is “reusable” but not usable
I’ve seen systems where:
Changing one line required touching five files and updating ten tests.
That’s not scalable. That’s brittle.
Tests Don’t Replace Understanding
Another uncomfortable truth, Tests don’t magically make code maintainable. Yes, tests are valuable. But when the only way to understand behavior is to:
- read mocks
- inspect spies
- reverse-engineer test setups
which means something is wrong.
A maintainable system lets you:
- read the code
- run it
- log it
- debug it
Tests should support understanding — not replace it.
So What Does Make Code Maintainable?
After years of writing, breaking, and fixing code, here’s what I now value:
- Predictability – If something breaks, i know where to start looking.
- Cohesion – Related logic lives together. No treasure hunts across the codebase.
- Explicitness – Important decisions are visible, No “magic” happening behind my back.
- Simple Control Flow – Can follow the path without mental gymnastics
- Minimal Abstraction – Abstract when it helps, not when a book says so.
The Real Lesson
Clean Code promised certainty:
“Follow these rules and your code will be good.”
But reality is messier.
Maintainable code is not about purity. It’s about survivability.
Code that:
- can be understood months later
- can be fixed by someone else
- can tolerate mistakes
That’s the kind of code that lasts.
And honestly?
I’ll take boring, obvious, debuggable code over “beautiful” abstractions any day.