January 2 2025
Writing Code People Can Understand
A simple way to choose names, structure, and abstraction level without turning code into an expensive puzzle.
Andrews Ribeiro
Founder & Engineer
4 min Intermediate Thinking
The problem
A lot of code technically works, passes tests, and still makes every engineer on the team slower.
That usually does not happen because the business logic is too hard.
It happens because every variable name needs decoding, every function mixes too many responsibilities, and every “elegant” abstraction asks for more context than it gives back.
That cost rarely shows up on the day of the commit.
It shows up later:
- when a production bug lands
- when someone else inherits the module
- when you come back three months later
- when the rule changes under pressure
Hard-to-read code charges interest.
Mental model
You write code for the machine to run, but also for people to read.
In practice, readability means making the intention obvious.
A useful question is:
Can someone on the team look at this and understand what it does without opening five more files?
If the answer is no, the problem is usually in the naming, the structure, or the level of abstraction.
Clarity does not mean childish code.
It means reducing unnecessary mental work.
Breaking it down
Four decisions help a lot here:
- Choose names that show intent.
activeUserssays more thanitems.calculateLateFeesays more thanprocess. - Keep each function focused. A function does not need to be tiny. It needs to be easy to follow.
- Keep related changes close together. If two parts almost always change together, splitting them too much can make the code harder to follow.
- Abstract only when it truly simplifies things. Repetition is not automatically the biggest problem. Sometimes a little duplication is cheaper than forcing people through a generic layer.
The goal is not to impress anyone.
The goal is to reduce the mental load for the person who has to read, change, and debug the code later.
A fifth rule also helps:
- Keep the main rule visible. If the most important flow disappears inside generic helpers, the reading experience gets worse even if every small piece looks “clean.”
Simple example
Look at this:
function p(u) {
return u.filter((x) => x.a).map((x) => x.n)
}
It may work.
But the reader has to reverse-engineer what p, u, a, and n mean before they can safely change anything.
A better version is:
function getActiveUserNames(users: User[]) {
return users.filter((user) => user.isActive).map((user) => user.name)
}
Now the rule is visible immediately: filter active users and return their names.
That is the difference between code that merely works and code that is easy to maintain.
Another common example:
async function h(req) {
const u = await repo.get(req.params.id)
if (!u) throw new Error("not found")
if (!u.active) throw new Error("inactive")
if (!u.email) throw new Error("missing")
return sender.send(u.email)
}
This can work, but it hides the intent.
A clearer version is:
async function sendActivationReminder(userId: string) {
const user = await repo.get(userId)
if (!user) throw new Error("user not found")
if (!user.isActive) throw new Error("user inactive")
if (!user.email) throw new Error("user without email")
return sender.send(user.email)
}
It is not more advanced.
It is just clearer.
Common mistakes
- abbreviating variables for no good reason
- breaking a simple flow into too many tiny functions and losing the main story
- creating generic abstractions too early
- treating DRY as a religion even when removing duplication hurts readability
- turning a simple flow into a scavenger hunt across files
How a senior thinks
Anyone who has maintained production code learns this quickly: the hard part is not only writing the code. The hard part is understanding it later when someone needs to change it under pressure.
That changes the question:
If I or someone else comes back to this under pressure, will the intention still be obvious?
That question usually leads to better choices: accept a little duplication, keep the main flow visible, and avoid too much abstraction too early.
What the interviewer wants to see
In interviews or code reviews, clarity matters a lot. People evaluating you will notice:
- whether your names help or hurt the reading
- whether the main flow is easy to follow
- whether you know when to avoid unnecessary abstractions
- whether you can explain the trade-off between duplication and clarity
A strong answer usually sounds like this:
“I try to keep the intention visible in the names, in the main flow, and in the abstraction level. If I have to choose between a little clear duplication and a generic abstraction that hides the rule, I usually choose clarity.”
Good code does not make the reader feel stupid. It makes the intention feel simple.
Quick summary
What to keep in your head
- Readable code reduces the cost of change, not just the cost of reading.
- Names, structure, and abstraction level matter more than clever syntax.
- A little clear duplication is often cheaper than a generic abstraction introduced too early.
- In interviews and code reviews, clarity usually matters more than tricks.
Practice checklist
Use this when you answer
- Can I choose names that reveal intent instead of implementation trivia?
- Can I keep the main flow visible without hiding everything behind helpers?
- Can I tell when an abstraction actually simplifies reading versus when it only feels reusable?
- Can I explain why I kept something duplicated for clarity?
You finished this article
Share this page
Copy the link manually from the field below.