Formatting
Formatting is not a matter of taste to be settled once and forgotten. It is one of the main channels through which code communicates, alongside naming and structure, and it keeps communicating for as long as the code exists, long after the feature it implements has been rewritten twice. A codebase that is consistently formatted reads as the work of people who pay attention to detail. One that is not raises the same question about everything else in it.
Vertical formatting
File size
Smaller files tend to be easier to hold in your head than larger ones. There is no hard ceiling, but a codebase built out of files in the low hundreds of lines, rather than thousands, is usually easier to navigate, because each file commits to one topic instead of several.
The newspaper metaphor
A well-written source file reads like a newspaper article: a name at the top that tells you what you are looking at, a high-level summary near the beginning, and increasing detail as you scroll down. Someone should be able to tell whether they are in the right file from the name alone, and get the gist from the first few functions without reading the whole thing.
Blank lines separate concepts, density implies association
Each blank line is a visual signal that one thought has ended and another is starting. Removing them collapses everything into an undifferentiated block that is much harder to scan, even though nothing about the logic changed.
The reverse also matters: lines that belong together - two related fields, a comment and the line it explains - should sit close together with nothing between them. Splitting up tightly related lines forces the reader to hold more open threads in their head at once than the logic actually requires.
Vertical distance
Concepts that are closely related should be kept close together in the file, and concepts that call each other should read in the order they are called. A few concrete rules follow from that:
- Local variables belong right above their first use, and loop control variables belong inside the loop statement itself, not declared several lines earlier.
- Instance variables belong in one well-known place, conventionally the top of the class. Where they go matters less than that everyone on the team knows where to look.
- A function that calls another should appear above the function it calls, so the file reads top-down, from what happens first to how each piece is implemented. A reader can then trust that the definition of whatever was just called is coming up shortly, instead of being buried somewhere earlier in the file.
- Functions with a strong conceptual relationship - a family of
assertTrue,assertFalse,assertEqualshelpers, for instance - should sit near each other even when they do not call one another, because the reason to group them is that they are doing variations of the same job, not that they share a call graph.
Horizontal formatting
Line length
Most lines in real codebases are short, and the ones that are not are usually a sign of something cleverer than it needs to be. Somewhere in the range of 80 to 120 characters is a reasonable ceiling; treat anything that regularly needs a reader to scroll sideways as a smell rather than a style choice.
Spacing communicates precedence and grouping
Whitespace around an operator signals how tightly two things are bound. Put space around a lower-precedence operator and none around a higher-precedence one, and an expression reads almost like the math it represents.
The same idea applies to a function call: no space between the function name and its opening parenthesis, because the two are one unit, but a space after each comma between arguments, because the arguments are separate from each other.
Resist aligning declarations into columns
Padding a list of field names or assignments with extra spaces so their values line up in a neat column looks tidy, but it draws the eye down the wrong axis - a reader ends up scanning the list of names without ever looking at the types, or the list of values without looking at what they're assigned to.
If a list of fields is long enough that misaligned names feel hard to scan, that's a signal the class itself has grown too large, not a reason to reach for alignment.
Indentation is not optional, even for one-liners
Indentation is how a reader sees the hierarchy of a file - what belongs to which class,
which method, which block - without reading every line of code. It is tempting to collapse
a short if, a short loop, or a short constructor onto one line to save vertical space, and
it is worth resisting that temptation. Once a scope has been collapsed onto a single line,
the next line added to it either breaks the illusion of simplicity or gets bolted on
awkwardly.
The expanded version costs three extra lines and buys back the ability to add a second statement to any of those bodies without first having to reformat the whole line.
Team rules over personal preference
Every programmer has opinions about where braces go and how many spaces a tab is worth. None of those opinions matter as much as the team agreeing on one answer and an automatic formatter enforcing it. A codebase that looks like it was written by one disciplined person is easier to read than one that looks like it was written by several people who each won an argument about style at different points in its history. Decide once, encode it into the project's formatter config, and stop relitigating it in review.