Early return pattern
Revision is an important part of writing prose. Often, an author will write their initial draft in a stream-of-consciousness fashion, dumping words on the page in the order that they come to mind. (In fact, that's exactly how I wrote the first draft of this article.) However, to aid understanding, it's important to go back and review what's been written with a more critical eye. Writing and editing are very different disciplines, to such a degree that a college professor once stated "write drunk, edit sober". When editing, you're no longer primarily thinking about yourself and the ideas you want to convey; instead, your focus is on the audience. How will the reader understand this passage? Do these concepts make sense to someone who's seeing them for the first time ever?
Lately I've been thinking about the similarities between writing prose and writing code. I may write more on this later, but I'd like to learn more about the similarities between programming language design and linguistics. One thing is clear to me, however: code produced during stream-of-consciousness writing benefits from a cleanup pass.
One of my favorite cleanup techniques is the "early return" pattern. This is used in a function to reduce the depth of nested logic. Here's an example of what a Mars rover might have in its codebase:
function decideAction() {
if (boulderAhead()) {
takePhoto();
turnLeft(90);
} else {
collectSample('soil');
accelerate();
}
}
Notice that all of the logic here is indented twice: once for the function body, and once for the if/else statement. However, a simpler version might look like:
function decideAction() {
if (boulderAhead()) {
takePhoto();
turnLeft(90);
return;
}
collectSample('soil');
accelerate();
}
This adjustment achieves a couple of goals:
- It emphasizes the "boulder ahead" branch as a special case. With this version, it's clear that soil sample collection and acceleration are the expected "default" behavior. This implies that finding a boulder is a special event, not the norm. (If the opposite were true, we could invert the condition and put takePhoto() and turnLeft() into the unindented function body.)
- It reduces the nesting depth of the code. This limits the amount of complexity that the reader has to hold in their head at one time. If they're not explicitly interested in the "boulder ahead" case, then they don't need to occupy precious brain space with it.
The "younger brother" of early return: early continue
You can also use this pattern in for
or while
loops! In this case, the continue
keyword in a loop behaves the same as return
in a forEach
callback. Example:
// Without early continue:
while (true) {
if (fuelRemaining === 0) {
soundAlarm('fuel_exhausted');
await refueling();
} else {
computeNavigation();
fireRockets();
}
}
// Using early continue:
while (true) {
if (fuelRemaining === 0) {
soundAlarm('fuel_exhausted');
await refueling();
continue;
}
computeNavigation();
fireRockets();
}
See also
- Mat Ryer, Code: Align the happy path to the left edge
- Leonel Menaia, Return Early Pattern
- The P42 JS Refactoring Assistant extension for VS Code offers "Introduce early return" as a refactoring option when editing an if/else statement.