Skip to content

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.