Functions#
Functions are the unit of intent. A good function does one thing, says what it does through its name and signature, and can be understood without reading its body.
One responsibility#
- A function does one thing. If describing it needs the word "and", split it.
- Keep it small enough to hold in your head. Whether it fits on a screen is a better test than a line count.
- One level of abstraction per function — don't mix high-level orchestration with low-level detail in the same body.
Signatures are contracts#
- Type the parameters and return value where the language allows. The signature documents intent and lets tooling catch misuse before it runs.
- Few parameters. A long parameter list signals a missing type or a function doing too much — group related arguments into an object.
- Avoid boolean parameters that select behavior (
render(true)). Split into two clearly named functions, or pass an explicit, named option. - Order parameters so the required ones come first — readers scan left to right.
Validate at the boundary#
- Reject bad input where it enters, before it travels deep into the call stack. A clear failure at the edge beats a baffling one three layers down. This is shift left applied to a single function.
- Inside a validated boundary, trust the data. At the edge, trust nothing.
Flow reads top to bottom#
- Return early. Put guard clauses for edge cases and exits at the top, then let the main path read straight down — no deep nesting.
- Prefer high-level constructs (iterate a collection directly) over manual, error-prone ones (index into it) when the index isn't needed.
- Handle every case. A branch over a closed set of values covers every member or carries an explicit default — an unhandled value is a silent failure.
Side effects at the edges#
- Prefer pure functions: the output depends only on the input, with no hidden state and no side effects. Pure functions are trivial to test and to reason about.
- Push side effects — I/O, mutation, network, time — to the edges of the system, and keep the core logic pure.
Gate destructive operations#
- Operations that create, change, or delete state offer a preview-or-confirm path before they act. Irreversible actions earn a deliberate gate. See Security.