"Clean code" advice has a reputation problem — some of it is dogma that makes codebases worse. Here's what's actually held up after years of maintaining production systems.
Holds up: name things for the reader, not the writer
// Bad — clear to you right now, opaque in six months
const d = calc(u, 0.08);
// Good — clear to whoever reads this next, including future you
const totalWithTax = calculateTotal(unitPrice, taxRate);Names are the cheapest form of documentation you'll ever write. Spend the extra ten seconds.
Holds up: functions should do one thing, but "one thing" is contextual
The rule "functions should be 5 lines long" doesn't hold up — plenty of correct, readable functions are 20-30 lines when they represent one coherent operation. What actually matters: a function shouldn't silently do two unrelated things (e.g., saveUser() that also sends an email as a side effect nobody asked for by name).
Doesn't hold up: avoid comments entirely
"Code should be self-documenting, comments are a code smell" is repeated a lot and is wrong in an important way. Comments explaining what the code does are often redundant. Comments explaining why — a workaround for a browser bug, a business rule that looks arbitrary, a decision that was deliberately made against the "obvious" approach — are some of the highest-value lines in a codebase.
// Intentionally not using Promise.all here — one slow request
// shouldn't block the others from completing (see incident #482).
const results = await Promise.allSettled(requests);Doesn't hold up: DRY at all costs
Premature abstraction to avoid three lines of duplicated code often creates a worse problem: a shared function that two callers depend on for subtly different reasons, which then needs a boolean flag, then another, until it's harder to read than the duplication would have been. Three similar lines in two places is fine. The rule of thumb: wait for a third occurrence before abstracting, and even then, check whether the two use cases are actually the same concept or just visually similar code.
Holds up: consistent formatting via tooling, not review comments
Bikeshedding over tabs vs. spaces or where braces go is a waste of human review time. Configure Prettier/ESLint (or your language's equivalent) and stop having the conversation. This is one of the highest-leverage, lowest-effort "clean code" wins available — it's a one-time setup cost that removes an entire category of code review friction permanently.
Holds up: optimize for deletion, not just addition
Code that's easy to delete cleanly — isolated, without tendrils reaching into unrelated parts of the system — is underrated compared to code that's easy to extend. Features get removed and rewritten far more often than most initial designs account for; a codebase where removing a feature means confidently deleting a folder is in much better shape than one where removal means archaeology to find every place that quietly depends on it.
Doesn't hold up: one class/interface per file, always
Rigidly enforcing "one exported thing per file" as a blanket rule often scatters a handful of genuinely related, small types across many files that would be easier to understand read together. The actual principle worth keeping: group by what changes together and gets read together, not by an arbitrary one-per-file count.
Holds up: make invalid states unrepresentable where the type system allows it
Rather than a single object with several optional fields that only make sense in certain combinations, a type that structurally prevents the invalid combination catches a whole category of bug at compile time instead of relying on every caller remembering a rule:
// Allows invalid combinations the type system can't catch
interface Request {
status: "loading" | "success" | "error";
data?: User;
error?: string;
}
// Nothing stops { status: "success", error: "oops" } from compiling
// The same states, structurally impossible to combine wrong
type RequestState =
| { status: "loading" }
| { status: "success"; data: User }
| { status: "error"; error: string };This isn't "more clean code dogma" so much as a genuinely different category of technique — a discriminated union like the second version means a caller literally cannot construct the invalid state, rather than merely being told not to in a comment or a runtime check.
Doesn't hold up: premature interfaces for every implementation
"Always code to an interface, not an implementation" made more sense when swapping implementations was a common, anticipated need (multiple database backends, pluggable strategies). Applied reflexively to every class with exactly one implementation and no planned second one, it adds a layer of indirection — jump to definition now lands on an interface, not the actual logic — with no real flexibility gained, since nothing is actually plugging in a second implementation. Add the interface when a second implementation is real, not speculative.
Holds up: early returns over nested conditionals
// Nested — harder to hold the conditions in your head
function getDiscount(user) {
if (user) {
if (user.isActive) {
if (user.orders > 10) {
return 0.1;
}
}
}
return 0;
}
// Early returns — each condition is handled and dismissed
function getDiscount(user) {
if (!user) return 0;
if (!user.isActive) return 0;
if (user.orders <= 10) return 0;
return 0.1;
}Both versions are correct. The second reads top-to-bottom as a list of disqualifying conditions, each fully resolved before moving to the next — the first requires holding three levels of nesting in your head simultaneously just to find the one line that matters.
Readable code and efficient code aren't in tension nearly as often as people assume — understanding Big O notation well enough to spot a needless nested loop is itself a form of the same discipline: naming and structuring code so the next reader (including future you) can actually reason about what it does and how it scales.
| Aspect | Holds up | Doesn't hold up |
|---|---|---|
| Naming | Name for the reader, not the writer | Avoid comments entirely — "why" comments are genuinely valuable |
| Function size | One coherent thing, whatever length that takes | A fixed line-count rule ("5 lines max") |
| Duplication | Wait for a third occurrence before abstracting | DRY at all costs, even for coincidental similarity |
| File structure | Group by what changes together | Rigid "one class per file", always |
The actual throughline
Every rule above that holds up shares one property: it optimizes for the next person who reads the code, who is very often you in six months with no memory of why you did something. Every rule that doesn't hold up is one that optimizes for an aesthetic ideal instead of that reader's actual experience.
Related reading
- Big O Notation Without the Math Panic — shares tags: programming, productivity (same category).
- Python Virtual Environments: venv, pipenv, poetry, or uv? — shares tags: programming, productivity.
- Async Python with asyncio: A Practical Introduction — shares tags: programming.
- Understanding Cloud Cost Optimization Basics — shares tags: productivity.
- Core Web Vitals Explained: What Actually Affects Your Score — shares tags: programming.