skip to content

Writing Clean Code

Code-in-the-small: how a single name, function, comment or error path is written so intent is visible at a glance. This is the material reviewers comment on daily and the level a take-home assignment is judged at.

part ofSoftware design & architectureoverview, primer and where to startread it →
on this pageshow

questions

page 2 of 2

Comments and documents drift out of sync with the code they describe. Explain the mechanism behind this drift and the techniques that keep explanatory material trustworthy over time.

level: seniorimportance: should knowfreq 38%

basics

~20 s

Code is verified by compilers and tests; prose is verified by nobody. So edits update the code and skip the comment, and the comment quietly becomes false. Fixes: fewer comments, put facts in code/tests/types, and review comments as code.

open as a page

When should a function return a Result/Either type instead of throwing an exception, and what are the trade-offs of each style?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Throw for unexpected failures the caller usually can't handle locally — bugs, infrastructure outages. Return a Result/Either when failure is an expected outcome the caller must decide about, because it puts the failure in the type signature and can't be forgotten.

open as a page

What does file and function length tell you about a codebase, and why do experienced engineers treat a growing file as a design signal rather than a formatting problem?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Small files are easier to understand than large ones. A file that keeps growing usually means it has taken on several responsibilities, so the fix is splitting it up — not adding more comments, headers, or blank lines.

open as a page

What is a hidden side effect in a function, why is temporal coupling a particularly dangerous form of it, and how do you design the problem away?

level: seniorimportance: should knowfreq 48%

basics

~20 s

A hidden side effect is a change the function makes that its name doesn't advertise — mutating a global, a parameter, or session state inside something that looks like a check. It breaks callers' assumptions and creates order dependencies between calls that nothing enforces.

open as a page

In what way is a hard-to-choose name design feedback, and what makes renaming expensive once an identifier has crossed a module or published-API boundary?

level: principalimportance: should knowfreq 30%

basics

~20 s

If you cannot name something without "And" or a vague word like "Manager", it probably does more than one job — the naming problem is really a design problem. Renaming is free inside a function, but a name in a public API, database column, event field, or metric is a contract others depend on.

open as a page

How should a name's length and specificity relate to the size of its scope, and what makes a name "searchable" and "pronounceable"?

level: seniorimportance: nice to knowfreq 35%

basics

~20 s

The further a name travels from its declaration, the more descriptive it must be. i is fine in a three-line loop; a module-level export needs a full phrase. Searchable means findable by text search — MAX_RETRIES can be found, 3 cannot. Pronounceable means you can say it aloud.

open as a page

When does hiding an external dependency behind your own abstraction stop paying for itself, and how do you decide where to place - or not place - a boundary?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

A boundary pays off when the thing behind it is likely to change, is hard to test, or would otherwise spread everywhere. It stops paying when the abstraction just mirrors the dependency, when the dependency is a stable standard, or when its behaviour leaks through anyway.

open as a page

How would you introduce complexity thresholds as a CI quality gate across a large legacy codebase without stalling delivery or provoking metric gaming?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Do not fail the build on existing code. Freeze current violations as a baseline and enforce the threshold only on new and changed code, so the codebase improves as it is touched instead of demanding a big-bang cleanup.

open as a page

You own engineering standards for a large multi-team codebase. How would you decide which knowledge belongs in inline comments versus other artifacts, and how would you keep TODO markers from decaying into noise?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Give each kind of knowledge one home: code and tests for behaviour, comments for local why and hazards, commit history for change history, issue tracker for planned work, ADRs for decisions, generated reference for public APIs. Require TODOs to link an issue and expire them.

open as a page

How would you design a consistent error-handling strategy across a multi-service system so failures are diagnosable, retryable where safe, and never silent?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Agree one error taxonomy (client error / business rejection / transient infrastructure / bug), give each a stable code and a retryable flag, translate to transport in one place per service, log once with a correlation id, and make retries safe by requiring idempotency.

open as a page

An experienced engineer joins a team whose committed formatting standard conflicts with their own strong preferences. How should this be handled, and what principle governs whose style wins?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

The team standard wins. Code should look like one person wrote it, so personal preference gives way to the committed convention; if you disagree, propose a change to the standard rather than formatting your own files differently.

open as a page

"Extract till you drop" produces very small functions. As a technical leader, how do you decide when further decomposition stops paying off, and what team-level policy do you set instead of a line-count rule?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Stop when a helper isn't a nameable concept — when its name just paraphrases the caller, it has one caller, or it needs many parameters to rebuild the caller's context. Set a policy on naming, cohesion, and abstraction level in review; use line-count linting only as a smell trigger, never as a gate.

open as a page

showing 31–42 of 42