How, Not What: The Real Meaning of Declarative Code

Why a for loop forces you to mentally run the code to understand it, and what it actually means for code to be declarative instead of imperative.

September 1, 20263 min read2 / 10

Say you need to add up the price of everything in a shopping cart. Most people, regardless of experience, picture almost the same code:

JavaScript
function cartTotal(items) { let total = 0; for (let i = 0; i < items.length; i++) { total += items[i].price; } return total; }

This works. It's also imperative: code that spells out how to do something, one step at a time.

Why a For Loop Costs More to Read Than It Looks Like

To understand what this function does, you can't just glance at it. You have to run it in your head: start total at zero, look at item zero, add its price, move to item one, add its price, keep going until the loop ends.

That mental replay is the real cost. Computers are built to execute steps quickly and perfectly. Brains aren't. Any code that forces a reader to simulate execution just to understand its purpose is asking them to do the one thing they're worst at.

Compare that to a declarative version, code that states what you want instead of narrating every step to get there:

JavaScript
function cartTotal(items) { return items.reduce((total, item) => total + item.price, 0); }

You don't have to trace an index or an accumulator by hand. reduce is doing the same looping underneath, but the code you're reading only states the outcome: fold this list down into one total. This series comes back to reduce properly once it covers list operations in depth. For now, the shape is the point, not the method name.

It's a Spectrum, Not a Switch

Nothing here is a strict rule that a for loop is always wrong. Imperative and declarative aren't two boxes, they're two ends of the same line.

More imperative
More declarative

Someone used to writing raw loops sees .reduce() and calls it declarative. Someone used to functional code all day sees that exact same .reduce() and might call it imperative, compared to something even higher-level. Where a piece of code sits on that line depends on what you're comparing it to.

What matters isn't hitting some perfect declarative score. It's noticing, each time, whether you're making the reader simulate steps that the code could have just stated as an outcome instead.

The Same Idea Fixes Bad Code Comments Too

This isn't only about loops. It shows up in comments, too.

JavaScript
i++; // increment i

That comment describes how, and the code already says that. It adds nothing.

A comment earns its place by explaining why, the part the code can't say on its own. Why increment by one and not two? Why does this discount apply before tax instead of after? That's worth writing down. Restating a line in English isn't.

The clearest sign your code needs a comment at all is that it couldn't state its own purpose declaratively. The better fix, when you can manage it, is writing code that doesn't need the comment in the first place.

Why This Matters Before Anything Else in This Series

Every technique this series covers, purity, composition, immutability, is really the same move applied in a different spot: replace a paragraph of "first do this, then do that" with one expression that states the outcome.

Understanding why that trade is worth making is what makes the rest of this series click instead of feeling like arbitrary rules. The next question worth answering is what actually counts as a function in the first place, and it turns out that bar is higher than most code clears.