Writing Code Humans Can Read: Naming, Structure and the Cost of Cleverness
You write a line once. Someone reads it fifty times, and usually in a hurry.

You write a line once. Someone reads it fifty times, and usually in a hurry.

There is a particular feeling that arrives about six months into any codebase. You open a file you wrote, read a function you have no memory of writing, and spend eleven minutes working out what it does. The author was you. You had context then. You do not now.
This is the actual argument for readable code, and it has nothing to do with elegance. Code is written once and read constantly — during reviews, during debugging, at eleven at night when something is broken and nobody who wrote it is awake.
None of what follows is difficult. It is a set of small habits that compound, and the teams that hold to them move noticeably faster after the first year.

A good name removes the need for a comment, a lookup and a guess. A bad one is a small tax collected every time anyone reads the line.

The rule that holds up best: a name should say what the thing is or what it does, in the vocabulary of the domain, without abbreviation. d is meaningless. daysSinceLastLogin is not, and it is not meaningfully longer to type in any editor written this century.
isActive, hasUnpaidInvoice, canEdit. If a name needs not in front of it to make sense, invert it.calculateTotal, sendReceipt, parseAddress. A function named data tells you nothing about what it will do to your afternoon.users holds many, user holds one. This one rule prevents a startling number of bugs.if (status == 3) means nothing; if (status == Status.CANCELLED) means everything.If naming a function is hard, that is information. It usually means the function does more than one thing, and the difficulty of the name is the difficulty of the concept leaking out. Split it, and the names arrive by themselves.
Forget line-count rules. The real test is whether you can hold what a function does in your head while reading it. A function that fits on a screen and does one identifiable thing passes; one that requires scrolling and mental bookkeeping does not.
The most common structural improvement in any codebase is extracting a well-named block of a long function into its own small function. You have not reduced the total lines. You have replaced fifteen lines of detail with one line of intent, and intent is what readers are looking for.
Deeply nested conditionals are hard to read because every line requires you to remember which branches you are inside. Handling the exceptional cases first and returning immediately flattens the whole structure.
| Pattern | What the reader has to hold | Result |
|---|---|---|
| Nested if inside if inside if | three conditions, simultaneously | re-read to be sure |
| Guard clauses, then the main path | one condition at a time | read once, top to bottom |
| Early return on error cases | the happy path is unindented | the normal case is obvious |
A comment restating the code is worse than no comment, because it will eventually be wrong. The code changes; the comment does not. Now the file contains a confident lie.
Good comments explain the things the code genuinely cannot: why an obvious approach was rejected, which external constraint forces an odd shape, what the units are, why a sleep is exactly 300 milliseconds. Those comments stay valuable for years.
Write comments for the decision, not the instruction. The next reader can see what the line does. They cannot see the three approaches you tried first.
— A rule that ages well
Every codebase has a line that its author is quietly proud of — a dense one-liner that does four things through a chain of operations nobody else can follow at speed. It is genuinely impressive. It is also the line where bugs go to hide.
Clever code optimises for the moment of writing. Obvious code optimises for every moment after. When a change is needed at speed, obvious wins, and it is not close.
A payments service had a single expression that computed refunds: nested ternaries, a chained map and filter, and two clever short-circuits. It was correct, tested, and eight months old.
When tax rules changed, three engineers spent most of a day reasoning about it. Nobody was confident enough to change it. In the end it was rewritten as nine plain lines with named intermediate values — and the rewrite immediately exposed an edge case for partial refunds that the original had been getting wrong all along.
The clever version had not been wrong because it was clever. It had stayed wrong because nobody could read it well enough to notice.

Tabs or spaces, where the brace goes, how long a line may be — these arguments have consumed more engineering hours than most outages. The correct answer is that it does not matter, and that inconsistency does.
Adopt a formatter, run it automatically, and stop discussing it. A codebase where every file looks the same is one where the reader's attention goes to logic instead of layout.

utils is a place where unrelated code accumulates until nobody can name what is in it.Refactoring for readability is not free. Rewriting a working, well-tested module purely because you would have written it differently spends real time and risks real bugs. Improve code as you touch it for other reasons; leave the rest alone.
Readability is the one quality a code review is genuinely well suited to test, because the reviewer is the reader. If someone competent cannot follow a change without an explanation in the comments, the change is not clear enough — regardless of whether it works.
That works only when reviews are about the code rather than the author. Our guide to code reviews that improve teams covers how to keep that line.

Name things for what they are, keep functions small enough to hold in your head, flatten nesting with early returns, comment the why, prefer obvious over clever, automate formatting, and organise by feature so people can find things.
None of this is about aesthetics. It is about the total cost of a codebase over years — how quickly a new person becomes useful, how confidently an urgent fix can be made, how often a change breaks something nobody expected.
Write for the tired stranger who will read this at midnight. Often enough, that stranger is you.
Tap a star to share what you thought.
No ratings yet
Names that state intent in the language of the domain, functions small enough to understand in one pass, shallow nesting, comments that explain reasoning rather than mechanics, and consistent formatting across the codebase.
Short enough that you can hold what it does in your head while reading it. Line-count rules miss the point: a 40-line function doing one clear thing is fine, and a 12-line function doing three is not.
Comments that restate what the code does usually are — the code should say that itself. Comments explaining why a decision was made, what constraint forces an odd shape, or why an obvious approach fails are valuable and hard to replace.
Sign in to join the conversation.
Loading responses…
Have a story, idea, or something valuable to share? Join The Blog Story for free, publish your content, reach more readers, and earn a share of advertising revenue from eligible content.
Create quality content. Grow your audience. Grow your earning potential.
Improve it when you are already changing that area for another reason. Rewriting working, tested code purely on stylistic grounds spends real time and introduces real risk for a benefit that is hard to measure.
An early return that handles an exceptional or invalid case at the top of a function, so the main logic that follows runs unindented. It flattens nesting and makes the normal path the most visible thing in the function.
Because a bare value like 3 or 86400 carries no meaning, so readers must infer it and every use must be updated separately when it changes. A named constant states the intent and gives you one place to change.
Yes, mostly because it ends a recurring argument and makes every file look alike. When formatting is automatic, review comments are about logic instead of whitespace, and diffs show only real changes.
Group by feature rather than by technical type, keep single-use helpers next to the code that uses them, make the entry point easy to find, and avoid catch-all files like 'utils' that accumulate unrelated code.