Technique: Paragraphs of Code
...a way to clarify code structure by visually grouping related forms.
What it looks like
A paragraph, in code, is a group of lines separated from neighboring lines by blank space.
foo();
bar();
baz();
quux();
kludge();
Example 1
Before
Here is an excerpt from a index.ts
file that exports values and types from several other files:
export { Text } from "./display/Text"
export type { TextProps, CardinalDirection } from "./display/Text"
export { Theme } from "./display/Theme"
export type { Filled, Stroked } from "./display/Theme"
export { MovablePoint } from "./interaction/MovablePoint"
export type { MovablePointProps } from "./interaction/MovablePoint"
export { useMovablePoint } from "./interaction/useMovablePoint"
export type {
ConstraintFunction,
UseMovablePoint,
UseMovablePointArguments,
} from "./interaction/useMovablePoint"
Source: Mafs, by Steven Petryk, blank lines removed
There is a pattern here: each file has a pair of export statements associated with it: one
for values and one for types. We'd like to maintain the invariant that each file is listed in the index.ts
file only
once (so you can find all its exports in one place) and that there is exactly one value export and one type
export for each file.
However, it is not easy to see from the structure of this code (from which I removed the blank lines) that this invariant exists.
After
Here is the code as it actually appears in the repo. It's more readable than the example above, due to the use of paragraphs that group together the related exports:
export { Text } from "./display/Text"
export type { TextProps, CardinalDirection } from "./display/Text"
export { Theme } from "./display/Theme"
export type { Filled, Stroked } from "./display/Theme"
export { MovablePoint } from "./interaction/MovablePoint"
export type { MovablePointProps } from "./interaction/MovablePoint"
export { useMovablePoint } from "./interaction/useMovablePoint"
export type {
ConstraintFunction,
UseMovablePoint,
UseMovablePointArguments,
} from "./interaction/useMovablePoint"
Source: Mafs, by Steven Petryk
A paragraph says "these lines are more closely related to each other than they are to what's around them."
Counterexamples
Simply breaking up code into arbitrary chunks isn't helpful—it's counterproductive.
Caveats
Paragraphs work best as a code organization technique when there's only one obvious way of grouping things. In the example above, we grouped exports from the same file together into a paragraph. But another dimension is type exports versus value exports, and we could have organized the code along those lines instead, like this:
export { Text } from "./display/Text"
export { Theme } from "./display/Theme"
export { MovablePoint } from "./interaction/MovablePoint"
export { useMovablePoint } from "./interaction/useMovablePoint"
export type { TextProps, CardinalDirection } from "./display/Text"
export type { Filled, Stroked } from "./display/Theme"
export type { MovablePointProps } from "./interaction/MovablePoint"
export type {
ConstraintFunction,
UseMovablePoint,
UseMovablePointArguments,
} from "./interaction/useMovablePoint"
In this example, it doesn't matter much which way we organize the code; we can get the information we want with either structure. But in more complicated situations, paragraphs don't cut it. TODO: example.
Related Techniques
- ArrangeActAssert and ExtractTransformLoad are specific applications of this technique
- ScopingBlocks is often a good follow-up to this technique