Source Code Is Language
Code is read far more often than it is written, and “hard to read” has a measurable substrate: working memory (see Code and Cognition). We tend to discuss readability in terms of names, length, and nesting, but there is an older structure underneath all of it. A line of code that performs an action can be read as a sentence, and sentences have a word order. When the order matches the one our language gave us for free, the line reads itself; when it does not, the reader has to re-assemble the meaning before they can use it.
This part of the Hardly Readable series is about that order: a method call written in the grammar we already know - subject, predicate, object - reads almost for free, and the common ways of breaking that grammar are exactly the ways a call gets hard to read.
A familiar word order is a chunk
What makes code easy to read is chunking: working memory handles only a few units at once, so the less a line forces us to assemble from scratch, the lighter it reads (see Code and Cognition). Most of the chunks we use in code we had to learn - a loop header, a builder chain, a library idiom. One we never had to: our native language installed it for us. In English - and, with variations, in most languages a reader of this blog will think in - a plain action sentence runs subject - predicate - object, with a recipient, when there is one, following in a prepositional tail.
This order is not a style choice we make sentence by sentence; it is learned through years of use - worn in so deeply that we take a sentence shaped this way as a single chunk and parse it without spending any working memory on who is doing what to whom; the position of each word answers that. A line of code laid out in the same order inherits that head start; laid out in any other, it forfeits it, and the reader has to build the chunk instead of recognising one they already hold.
The interesting thing is that object-oriented method calls inherit the same shape almost by accident:
Account accountA = ...;
Account accountB = ...;
Money amount = ...;
accountA.transfer(amount).to(accountB);
The receiver accountA is the subject, the method transfer is the predicate, the argument amount is the object, and .to(accountB) supplies the recipient in a prepositional tail, exactly where English puts it. Read aloud it is the same sentence: accountA, transfer, amount, to accountB. A reader who knows the domain does not decode this call; they recognise it, the same way they recognise the English sentence. The grammar carries the role of each part, so none of it has to be held in working memory.
That is the baseline to protect. The two common ways to lose it are to pull the predicate to the front, and to turn the sentence around.
Predicate first: the procedural sentence
The first way to break the order is to lead with the verb:
transfer(accountA, amount, accountB);
This is the procedural form, and it is not wrong - whole languages and many good APIs are built on it. But notice what changed. The predicate now comes first, and the subject has been demoted to just another argument, sitting beside the object with nothing to distinguish them. The sentence is no longer “accountA transfers the amount to accountB”; it is closer to “transferring: accountA, amount, accountB” - a verb followed by a bag of nouns whose roles the reader has to assign.
The cost shows up the moment there is more than one argument:
transfer(accountA, accountB, amount); // which account is debited?
transfer(accountB, accountA, amount); // ... or was it the other way?
transfer(amount, accountA, accountB); // ... or is this more natural?
With the predicate in front, argument position is the only thing encoding role, and position is exactly what working memory is bad at holding. Was it transfer(from, to, amount) or transfer(to, from, amount)? Nothing in the call answers that; the reader has to recall the signature or open the method. The subject-predicate-object form rarely raises the question, because accountA.transfer(amount).to(accountB) puts the agent where an agent belongs - in front of the verb - and gives each remaining part its own slot instead of a shared argument list:
accountA.transfer(amount).to(accountB);
Now accountA is unmistakably the one acting, and amount and accountB are each attached to their own verb - transfer takes what moves, to takes where it goes - so there are no argument positions left to confuse.
The preposition earns its place here: it does not merely separate the arguments, it names the grammatical role each one plays. .to(accountB) marks accountB as the goal of the action - the dative object - exactly the way the word to does in English. Strip that preposition out and fold the party into a positional slot, as transfer(accountA, amount, accountB) does, and the marker is gone: nothing on the line says which noun is the accusative object being moved and which is the dative party it moves to. A role a preposition would have stated outright now has to be recovered from the signature.
The predicate-first form is most defensible when there is no real subject at all (a pure computation like max(a, b)); it gets steadily harder to read as the first argument turns into a disguised subject.
Object first: the passive sentence
The second way to break the order is more subtle, because the result still looks object-oriented. Here the object of the action becomes the receiver, and the real agent is pushed into the arguments:
amount.transferredBy(accountA, accountB);
Read as a sentence this is object, predicate, subject: the amount, transferred, by accountA, to accountB. It is the passive voice. English allows it too (“the amount was transferred by accountA to accountB”), and passive constructions generally take more effort to process - the reader meets the thing being acted on before they know who is acting, and has to hold it unattached until the agent turns up among the arguments. Put the agent back in front and the line reads forward again: accountA.transfer(amount).to(accountB).
The same misordering recurs across domains, always with a passive participle in front:
order.placedBy(customer); // object . predicate ( subject )
report.generatedBy(service);
message.sentBy(user);
Each of these reads backwards. The receiver is the thing that is acted upon, so the line opens with a passive participle (placedBy, generatedBy, sentBy) - itself a tell, the way a conjunction in a name is a tell for a literal composition (see Literal Composition Is Not Abstraction). The agent shows up last, in the position the grammar reserves for the object. The reader has to mentally swap the two before the sentence makes sense:
customer.place(order); // subject . predicate ( object )
service.generate(report);
user.send(message);
The active forms are not shorter, but they are in the order the reader already parses for free. The agent leads, the verb follows, the thing acted upon comes last - no swap, nothing held unattached.
The discussion so far has been about the order of the parts within a sentence. There is another question, though, that matters just as much: what kind of sentence the method call is in the first place.
Is there a subject at all
Everything so far rested on a quiet assumption - that the line is a statement about who does what, so there is a subject to put in front. But a method call can be read two ways, and only one of them has a subject.
Take the smallest example:
dog.eat(meat);
Read as a statement it is the subject-predicate-object sentence from the first half of this article: the dog eats the meat - dog is the subject, eat the predicate, meat the object. But just as naturally it is a command: “Dog, eat the meat!” - an instruction we send to the dog. The two readings even line up with the two families of language we already know: the statement describes what is the case, the way a declarative line does, while the command orders something done, the way an imperative one does.
The difference that matters for reading is this: a command has no subject. The imperative mood drops it - “Eat the meat!” names no agent. The one doing the commanding is us, the caller, and the caller never appears in the line. So under the command reading dog is not the subject at all - it is the addressee, the one being told. The same slot in front of the dot does double duty: under a statement it holds the subject, under a command the addressee.
A statement carries a subject; a command does not. And a line without a clear subject reads worse, because the subject is what states who stands in what relation to what. So the most readable calls are the ones that work both ways at once. dog.eat(meat) is such a line: a true statement (the dog eats the meat) and a sensible command (“Dog, eat the meat!”). The statement reading gives the line a subject and says plainly who acts on what; the command reading explains why it can stay so short. When both hold, the relation is right there to read.
A call that reads only as a command loses that. The everyday case is an accessor:
dog.getRace();
“Dog, get your race!” works as a command, but the statement collapses - “the dog gets a race” is false or means something else. With no statement reading there is no subject and no relation on the line, only an order. Drop the generic verb and the statement comes back:
dog.race(); // "the dog's race" - a description again, a relation we can read
The point is not that commands are bad - they are how objects get told what to do - but that a line which also reads as a statement is easier than one that does not, because the statement is where the subject, and with it the relation, becomes visible.
Why this is a working-memory question, not a taste question
Word order and sentence mood look like matters of taste. They are not. Each broken form makes the reader consciously perform a piece of parsing that the natural form does for free:
- Predicate first demotes the subject into a bare argument list with no prepositions to mark roles, so position alone encodes which argument is the agent and which object is which, and the reader recalls or opens the signature.
- Object first presents the thing acted upon before the actor, so the reader holds it unbound until the agent arrives at the end, then reverses the pair.
- An imperative drops the subject from the line, so the line stops stating who acts on what; the reader supplies the missing agent instead of reading the relation off the word order.
Each is a small cost on a single line. But reading is thousands of these lines, and a cost that recurs on every call is exactly the steady drain on working memory that makes a codebase feel heavy without any one line looking wrong. None of these costs is large in isolation; their effect comes from repetition. The reader pays them again and again until what should have been effortless recognition becomes continual reconstruction. The fix is rarely a new abstraction or a shorter name. It is to put the agent in front of its verb when the line is a statement, and to let the addressee own the verb - and accept that the subject is gone - when the line is a command.
Conclusion
A method call is a sentence, and the grammar we already carry decides how much of it we read for free. subject.predicate(object) inherits our native word order, so a reader recognises the line instead of decoding it; every way of breaking that order - predicate first, object first, or a call that reads only as a command - makes the reader do the assembly by hand. The fix is not a new abstraction but the same cheap move each time: put the agent in front of its verb when the line states who does what, and let the addressee own the verb when the line gives an order.
More
For related discussion and background, see:
- Code and Cognition - why “hard to read” has a measurable cognitive cost, and what chunks and working memory have to do with reading code
- Fluent Interface (Martin Fowler) - the idea that an API can be designed so calls read and flow like prose, the readable end of the spectrum this article maps