Most code is written once and read dozens of times. By the original author six months later, by a colleague tracking down a bug, by a future maintainer who has never met you. When you write code, you are writing a message to people you have not yet met.
That framing changes everything.
Names Are the First Draft
A variable named d tells you nothing. A variable named daysSinceLastLogin tells you exactly what it is, where it came from, and roughly what values it holds.
Naming is the first draft of documentation. It happens before comments, before tests, before architecture. And unlike comments, names cannot drift out of sync with the code they live in — they are the code.
// Bad: what is d? what is u?
const d = Date.now() - u.ts;
if (d > 86400000) logout(u);
// Good: the code reads like a sentence
const daysSinceLastLogin = (Date.now() - user.lastLoginAt) / MS_PER_DAY;
if (daysSinceLastLogin > 1) logoutInactiveUser(user);
The second version is longer. It is also obviously correct on first read, which means it is cheaper to maintain.
Functions Should Do One Thing
A function that validates an email, saves it to a database, and sends a welcome message is three functions in a trench coat. When it breaks — and it will — you will not know which part is misbehaving.
The discipline is to give each function a name you can say aloud without pausing to explain. If the name requires the word “and”, split the function.
// Harder to test, harder to reuse, harder to trust
async function handleSignup(email: string) {
if (!email.includes('@')) throw new Error('Invalid email');
await db.insert('users', { email });
await sendEmail(email, 'Welcome!');
}
// Each piece is testable and composable
function validateEmail(email: string): boolean {
return email.includes('@') && email.includes('.');
}
async function createUser(email: string): Promise<User> {
return db.insert('users', { email });
}
async function sendWelcome(user: User): Promise<void> {
await sendEmail(user.email, 'Welcome!');
}
The second version requires more lines. You get back composability: createUser can be called in tests without sending emails. sendWelcome can be reused for re-engagement campaigns. validateEmail can live in a utility module shared across the codebase.
Comments Explain Why, Not What
The worst comment restates the code in English:
# increment counter by 1
counter += 1
This adds noise. The code already says what it does. What the code cannot say is why.
# Cloudflare KV has eventual consistency; we increment optimistically
# and reconcile on the next cron run rather than blocking the request
counter += 1
That comment is worth keeping. It preserves the reasoning behind a decision that would otherwise look arbitrary. Six months from now, someone will want to “fix” the optimistic increment — this note will stop them.
Complexity Is a Debt
Every clever trick you write, you deposit into a debt account that someone else may have to repay. Bit manipulation, terse regular expressions, deeply nested ternaries — these are withdrawals from your reader’s cognitive budget.
The question is not “can I write this in one line?” The question is “will the next reader understand this in five seconds?”
// Clever
const roles = user?.roles?.reduce((a, r) => ({ ...a, [r]: true }), {}) ?? {};
// Clear
const roles: Record<string, boolean> = {};
for (const role of user?.roles ?? []) {
roles[role] = true;
}
The second version runs identically. It also tells a story: start empty, fill it in, done. No intermediate mental model required.
Structure Communicates Intent
The shape of code on the page carries meaning before a single token is parsed. A 300-line function signals: this is complicated, approach carefully. A module with 12 exported functions signals: this is a toolkit. A file that imports 40 things signals: this has too many concerns.
When code is hard to read, it is often because the structure is dishonest about the complexity it contains. Refactoring is, at its best, the act of making structure match reality.
Readable code is not a style preference. It is a professional courtesy extended to every person who will touch this code after you — including the version of yourself that will return to it tired, under pressure, three months from now.
Write for them.