Steven's Knowledge

Formatting

Vertical and horizontal layout, file organization, and conventions

Formatting

Formatting is communication. It signals which lines belong together, which are independent, where one idea ends and the next begins. Inconsistent formatting forces every reader to redo the parsing the original author already did.

The single most important rule is that the team agrees on the conventions and a tool enforces them. Disagreements over braces or indent width that survive into review distract from the work that matters. Configure a formatter (Prettier, gofmt, black, rustfmt, your language's equivalent) and let it decide.

The remaining sections describe principles that go beyond what a formatter handles.

Vertical Formatting

Files should be readable top to bottom

Order content the way a reader will encounter it. The top of a file should orient the reader: imports, module-level constants, the principal type or function. Details — private helpers, edge-case handlers — belong below.

A reader who scans the first screen should be able to summarize what the file is about. If they cannot, the file is either too large or organized incorrectly.

File length

There is no universal maximum, but most healthy files in production code fit within a few hundred lines. Beyond that, the file usually contains more than one thing and benefits from being split.

A file growing past a thousand lines is a strong signal — not that it must be split, but that the cost of not splitting is now substantial: large files make merge conflicts more likely, slow code search, and discourage refactoring because every change feels invasive.

Vertical openness between concepts

Use blank lines to separate logically distinct sections. The convention is:

  • One blank line between methods or top-level functions.
  • One blank line within a function to separate phases of work.
  • No blank line within a tight sequence of related statements.

The reader's eye uses these gaps as paragraph markers. Sprinkling blank lines randomly — or omitting them entirely — defeats this signal.

Code that belongs together should appear together. Specifically:

  • Variable declarations belong near their first use, not at the top of the function.
  • A helper function used by exactly one caller belongs near that caller.
  • A field used by one method may want to be a local variable instead.

The principle, from Clean Code, is vertical proximity: concepts closely related in meaning should be closely related in distance.

Vertical ordering within a class or file

Within a class, the convention from Clean Code and most style guides:

  1. Module / class-level constants and types.
  2. Public constructor / factory.
  3. Public methods, ordered to mirror the workflow.
  4. Private helpers, ordered to follow the public methods that call them.
  5. Private state.

In strongly typed languages, types referenced by methods may need to come first; that is a language constraint, not a violation of the principle.

The general rule, sometimes called the stepdown rule: the file reads top-to-bottom from high-level intent to low-level detail. A method calls helpers defined later; each helper calls more specific helpers below it; the most primitive functions are at the bottom.

Horizontal Formatting

Line length

Modern conventions land between 80 and 120 characters. The exact value matters less than the consistency. The reasons for keeping lines short are practical:

  • Side-by-side diffs and code reviews fit without wrapping.
  • Multi-pane editors stay usable.
  • A long line usually contains too many ideas; breaking it up reveals the pieces.

Letting one expression spill across many lines is preferable to one 200-character line with eight chained calls.

Horizontal openness

Whitespace inside lines emphasizes structure:

// Operators with low precedence get more space
const total = (price * quantity) + tax;

// Function arguments are separated tightly
sum(a, b, c)

// Assignment binds more loosely than function calls
let result = compute(value);

A formatter handles most of this. The remaining judgment is around expression complexity — when a line is doing several things, breaking it across multiple lines, with each line representing one step, is usually clearer than fitting it all in one.

Horizontal alignment

Aligning groups of declarations vertically by =, by type, or by argument:

const userId    = readUuid();
const userName  = readString();
const userEmail = readEmail();

This was once popular and is now generally avoided. The cost: every change to one line forces re-alignment of all neighbors, and the alignment is not preserved by editors or formatters. The benefit was readability that modern syntax-highlighting and consistent spacing already provide.

Indentation

Indentation marks scope. Two consequences:

  • Mixed tabs and spaces confuse tools and reviewers; pick one and enforce it.
  • Indent depth correlates with code complexity. A function with five levels of indentation is doing too much; reduce nesting via guard clauses, extracted functions, or restructured logic.

File Organization

One concept per file

The default unit is one class or one cohesive group of related functions per file. Multiple unrelated classes in one file forces readers to scan past content they did not come for.

The exception is a file of small, tightly related types — a discriminated union and its constructors, for example, or a set of related value objects. Cohesion is the test, not count.

File names that match content

A file named utils.js is a sign that no one knew what to call its contents. The cost: any utility added to the codebase plausibly belongs there, and the file accumulates unrelated code until it is unsearchable.

Concrete file names — dateRange.js, httpClient.js, priceCalculator.js — make code search work, make imports descriptive, and force the author to make a coherence decision before adding to the file.

Directory structure mirrors module structure

A directory should correspond to a meaningful grouping — a feature, a layer, a bounded context. The right grouping depends on the project, but the criterion is the same: someone navigating the tree should be able to predict where a given concept lives without having to grep.

Avoid:

  • Directories with one file in them. The directory adds nothing.
  • Directories named after technical layers (utils/, helpers/, services/, managers/) when the codebase already has a meaningful domain structure. The technical names hide the domain.

Style Conventions

The remaining decisions — naming case, brace placement, import ordering, trailing commas — are conventions, not principles. The team's job is to:

  1. Pick a defensible convention.
  2. Encode it in a formatter and a linter.
  3. Make the tools run automatically (pre-commit hook, CI check, editor on save).
  4. Stop arguing.

Time spent in review on formatting is time not spent on substance.

Comments and Code Layout

A comment placed above a block tells the reader what is coming; a comment placed at the end of a line annotates a specific element. The two forms have different conventions:

  • Block comments above the code they describe; same indentation as the code.
  • Inline trailing comments only when they fit on the same line; do not let them push lines past the line-length limit.
  • Group block comments visually with the code they describe — no blank line between the comment and the first line of code.

Section banners (// ===== USERS =====) inside a file are usually a sign that the file should be split. When they survive, keep them rare and consistent.

Pre-Commit Checklist

  • Is the file formatted by an automated tool, with the configuration committed to the repo?
  • Does the file read top-to-bottom from high-level intent to low-level detail?
  • Are concepts that belong together adjacent? Are concepts that do not, separated by blank lines?
  • Is each file scoped to one concept, with a name that reflects the contents?
  • Are lines within the team's agreed length limit?
  • Is indentation depth limited — are nested blocks rare?

On this page