Clean Code: Engineering for Humans
Code is read 10x more than it is written. A technical guide to Naming, Single Responsibility Principle, and why 'Clever Code' is actually technical debt.
“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” — Martin Fowler.
This quote is the foundation of specific engineering culture at Maison Code Paris. We do not optimize for “Lines of Code”. We do not optimize for “Typing Speed”. We optimize for Maintainability.
The compiler doesn’t care if you name your variable x or daysUntilExpiration. The CPU executes it in the exact same nanosecond.
But the Junior Developer who wakes up at 3:00 AM to fix a critical bug cares deeply.
If they have to spend 10 minutes deciphering your variable names, the company loses money.
Clean Code is an act of empathy.
Why Maison Code Discusses This
At Maison Code Paris, we act as the architectural conscience for our clients. We often inherit “modern” stacks that were built without a foundational understanding of scale. We see simple APIs that take 4 seconds to respond because of N+1 query problems, and “Microservices” that cost $5,000/month in idle cloud fees.
We discuss this topic because it represents a critical pivot point in engineering maturity. Implementing this correctly differentiates a fragile MVP from a resilient, enterprise-grade platform that can handle Black Friday traffic without breaking a sweat.
1. Naming: The Hardest Problem in Computer Science
Names should reveal intent. A variable name should tell you three things:
- What it does.
- Why it exists.
- How it is used.
The Rule of “No Mental Mapping”
Bad:
const d = 10; // days
const list = []; // users
The reader has to translate d -> days in their head. This uses cognitive load (RAM).
Good:
const daysSinceLastLogin = 10;
const activeUsers = [];
The code reads like English prose.
The Rule of Scope Length
- Small Scope (Loop): Short names are okay.
for (let i = 0; i < 10; i++)is fine. - Large Scope (Global/Class): Names must be explicit.
iis forbidden. UseglobalRetryCounter.
2. Functions: Do One Thing
The Single Responsibility Principle (SRP) applies to functions, not just classes. A function should have one level of abstraction.
Bad (The God Function):
async function registerUser(req) {
// 1. Parsing
const { email, password } = req.body;
// 2. Validation
if (!email.includes('@')) throw new Error('Invalid Email');
// 3. Database Logic
const user = await db.users.create({
email,
password: bcrypt.hashSync(password)
});
// 4. Email Logic
await mailgun.messages().send({ to: email, text: "Welcome!" });
// 5. Response Logic
return { status: 200, id: user.id };
}
This function changes if:
- The Database changes (Postgres to Mongo).
- The Email provider changes (Mailgun to SendGrid).
- The API format changes. It is fragile.
Good (The Orchestrator):
async function registerUser(req) {
const accountInfo = parseRequest(req);
validateAccount(accountInfo);
const user = await createUserInDatabase(accountInfo);
await sendWelcomeEmail(user);
return formatResponse(user);
}
Now, registerUser is just an orchestrator. It tells a story. The implementation details are hidden in small, focused functions.
3. Comments: The Failure of Expression
Top engineers write fewer comments, not more. Why? Because Comments are a Code Smell. If you have to write a comment to explain what the code does, your code is too complex. Refactor it.
Bad:
// Check if user is eligible for senior discount
if (user.age > 65 && user.purchases > 10 && user.country === 'FR') {
applyDiscount();
}
Good:
if (user.isEligibleForSeniorDiscount()) {
applyDiscount();
}
When to Comment?
Comment the WHY, not the WHAT. The code explains what happens. The comment explains the business context or the strange workaround.
// We use a 500ms delay here because the external Payment API
// has a race condition if queried immediately after creation.
// See Ticket #452.
await delay(500);
This comment saves the next developer from “optimizing” the delay away.
4. Conditionals: Avoid the Arrow Code
“Arrow Code” is when nested if statements create a shape like an arrow >.
It pushes the logic to the right side of the screen and makes it hard to track state.
Bad:
function processPayment(order) {
if (order) {
if (order.isPaid) {
if (order.amount > 0) {
// Logic
}
}
}
}
Good (Guard Clauses): Return early. Kill the function as soon as possible.
function processPayment(order) {
if (!order) return;
if (!order.isPaid) return;
if (order.amount <= 0) return;
// Logic (No indentation)
}
5. Primitive Obsession
Don’t pass raw strings and numbers around. Pass Objects that enforce rules.
Bad:
function bookFlight(origin: string, dest: string)
Is “NYC” valid? Is “New York” valid? Is “JFK” valid?
Good:
function bookFlight(origin: AirportCode, dest: AirportCode)
The AirportCode class ensures that only valid 3-letter IATA codes can exist in the system.
You make invalid states unrepresentable.
7. Testing as Documentation
The best documentation is a Unit Test.
Docs get outdated. Tests do not.
If you want to know how calculateDiscount() works, read the test cases.
it('should apply 10% for new users')it('should fail if cart is empty')Writing tests forces you to write Clean Code. If a function is hard to test, it is bad code. “I can’t test this because it queries the DB inside the function.” Refactor: Inject the DB dependency. Now it is testable.
8. The Code Review Culture
Code Review is not for finding bugs. That is what CI is for. Code Review is for Knowledge Sharing. It is a mentorship session. “Why did you choose a Set instead of an Array here?” “This pattern is interesting, can we use it in the User module too?” Senior Engineers should not just say “LGTM”. They should explain why something is good or bad. We require at least 1 “Nitpick” or “Suggestion” per PR. It forces engagement.
9. The Boy Scout Rule
“Always leave the campground cleaner than you found it.”
This is the antidote to Technical Debt.
You are fixing a bug in the Pricing module. You notice a variable named x.
Rename it.
You notice a function that is too long.
Split it.
Do not ask for permission. Do not create a ticket “Refactor Pricing”. Just do it as part of your work.
If every engineer cleans up 1% of the code they touch, the codebase stays pristine forever.
7. DRY vs WET (The Abstraction Trap)
We are taught DRY (Don’t Repeat Yourself).
It is a good rule. But it is dangerous.
If you see two pieces of code that look similar, you merge them into a shared function.
The Problem: Now both places rely on that function.
If Feature A needs a slight change to the function, you add a boolean flag.
function doThing(isFeatureA)
Then Feature B needs a change.
function doThing(isFeatureA, isFeatureB)
Soon, you have a God Function with 10 flags.
WET (Write Everything Twice) is often better.
Duplication is cheaper than the wrong abstraction.
If two features accidentally share logic but have different business reasons to change, copy paste the code.
8. The “Broken Windows” Theory
In criminology, if a building has one broken window that isn’t fixed, vandals will soon break all the windows. Code is the same. If you leave one messy function, the next developer will think: “This file is garbage anyway, I’ll just hack my fix in.” If the file is pristine, they will feel the social pressure to keep it pristine. Be the gardener. Weed out the mess daily.
9. Conclusion
Clean Code is not about aesthetics. It is about Economics. Messy code rots. It becomes “Legacy Code” that everyone is afraid to touch. Features take longer to build. Bugs appear more often. Clean Code is an asset. It allows you to move fast indefinitely.
Codebase a mess?
Is your velocity slowing down because of “Spaghetti Code”?
Clean Code is not about aesthetics. It is about Economics. Messy code rots. It becomes “Legacy Code” that everyone is afraid to touch. Features take longer to build. Bugs appear more often. Clean Code is an asset. It allows you to move fast indefinitely.
Write code for the human who will read it. Because 6 months from now, that human will be you.
Codebase a mess?
Is your velocity slowing down because of “Spaghetti Code”?