Skip to content
AtomicReps

You read it. You did not run it.

A lesson from The Model You Stopped Building. Play it above, or read it through below.

  • set -e did check a status. It was not the status that failed.

    The common read: set -euo pipefail stops a script at the first failing command, so a grep that matches nothing aborts the run before anything deploys.

    set -e did not miss anything. It inspected an exit status at the moment the manual says it does, and the status it inspected belonged to local, whose documented return is zero unless the name is invalid or the builtin was called outside a function, so the shell saw a success and moved to the next line. The rule fired. Nothing had failed as far as it could tell.

    The retro named the config file. The action item was a schema check on release.conf, which is worth owning and would not have changed one character of what happened: the file was correct, the read was correct, and the only broken thing was an exit status nobody printed. Six weeks later the same line shipped a second empty channel.

    Reciting the rule is a claim about a rule. The rule is about a place: which command's status the shell inspects, and when. Reading the script tells you set -euo pipefail is present and that whoever wrote it meant it. It does not tell you which of the statuses produced on that one line the shell decided to look at, and re-reading does not resolve it, because the answer is not in the text.

  • Splitting one line into two changed the exit status. It changed nothing else.

    The common read: I know how set -e behaves, so a rule I can recite is a rule I would have applied while reading this file.

    Name the exit status of a script identical to the last one except that local channel sits on a line of its own, with the assignment beneath it. Nothing else moves: same file, same grep, same missing key, same two echo lines, same header. The two scripts behave differently, and the difference is in neither one's meaning.

    The second exits 1 and prints nothing at all. An assignment with no command name in front of it takes the exit status of its last command substitution, so set -e sees the 1 that grep produced, leaves the function before the first echo reaches stdout, and takes the whole script down with it. One line became two lines and the release stopped happening. Every token a reviewer reads as meaningful is identical across the two versions.

    The review that approved the first version left one comment and it was about the function name. That is not carelessness. Naming is the part of a diff a reader can evaluate by reading, so it is the part that gets evaluated, and the part that decides whether the release runs sits somewhere reading does not reach.

    Knowing the rule was never the missing piece. The missing piece was which line the rule applied to, and that question has one cheap answer: run both, print $?, and the two versions stop looking alike. Ten seconds against a release that reports success. The trade prices itself on any change where a silent wrong action costs more than a rerun, and it fails to price itself on anything else.

  • forEach never waited. The count was reported before it existed.

    The common read: await inside the callback makes the loop wait, so the count on the line after it is the count once the work is done.

    The await did hold, and it held the wrong thing. It suspends the callback's own frame until the 10 ms pause resolves, and that frame is the one forEach had already walked away from: the specification's loop performs the call, binds nothing, and returns undefined. Three async calls become three promises with no holder anywhere, the loop finishes on the tick it started on, and the line beneath it reads a length that is still zero.

    Nothing was lost, and that is the second half of the answer. Swap forEach for a for...of and the same flush reports 3 on the same line, because a for...of body sits inside the frame the await suspends. Both were run. The forEach version reaches 3 as well, after the print, and a 50 ms instrument catches it there. The work happens. It happens after the only line that reports it.

    The incident doc said the drain had failed and asked for a retry on the flush path. The drain had not failed. Every id landed, moments after the line that said none of them had, and the retry that shipped writes each id a second time whenever the pauses run long. Nothing in that file was ever wrong, and a retry aimed at a count is a repair aimed at a report.

    async is a claim about a frame, and the frame is not always the one you are reading. Inside the callback the keyword does everything it says. At the boundary, whoever called the callback decides whether anything waits, and forEach decided that before this file was written. Reading the line tells you the await is there. It does not tell you who is holding the promise, and re-reading does not resolve it, because the answer is in the caller and the caller is a built-in.

  • You can explain every line. That is a different thing from naming what it prints.

    The common read: I can explain what this code does, so I know what it outputs.

    Explaining code and predicting code draw on different things, and only one of them can be checked. An explanation names the parts and says what each part is for, and every file on this screen has a correct one, short enough to give out loud, that anybody who has read the file could produce without hesitating or looking anything up. A prediction is a commitment to a value on an input, and it survives the run or it does not.

    Reading a familiar shape is fast because the shape is a chunk you already hold, and a chunk carries the rule you learned it with. It does not carry the rule's exception, because the exception was never in a file you had to read closely. Fluency is speed. Comprehension is what a prediction cashes, and no other step in a review asks for one.

    The approval on that release took four minutes and the timeline records the four minutes. It does not record that the approver, asked forty minutes later what the function returned when the key was missing, had nothing to say. No step in a review has ever asked for that answer and no field in any review tool holds it, so the one number that would have measured comprehension is the one number nobody collects.

  • A prediction takes ten seconds. Nothing else in a review measures comprehension.

    The common read: careful reading is the expensive part of a review, so running the code is an optional extra for the times there is room for it.

    Approve one file today without naming what it prints on the input it will actually see, and the defect that ships is the one no amount of careful reading was going to catch. A prediction costs about ten seconds. It is the only step in a review that can be wrong out loud, and that is the whole of its value: a wrong prediction is information, and a satisfied reading is not.

    What gets predicted is one value on one input, before the run. Not what the function is for, not whether the approach is right, not whether the names are good. A number, a string, a count, an exit status. The commitment has to be specific enough to lose, which is why "it looks correct" is not one and "it returns an empty list" is.

    The comment left on that review was about a variable name, and the reviewer was right about the name. Naming is the part of a diff that reading can evaluate, so it is the part reviews evaluate, and the ratio of naming comments to behaviour comments in any review tool measures what reading can reach rather than what mattered.

    The trade is real and it does not scale to everything. Predicting every value in a large change is not affordable, so the ten seconds buys one prediction per file, and the file that gets it is the one whose failure is not a rerun. The ticket that came out of that release was called config parsing improvements, which is the most accurate sentence anyone wrote that week and describes nothing that happened.

  • The check inherits the blind spot. Run it, then read it.

    The common read: parsing a payload and serializing it back is an identity, so an equality check against the literal I typed would catch it if it were not.

    The round trip is not an identity, and the check that was supposed to catch it is not a check. Parsing turns the text into a double, and above 2^53 - 1 not every integer is representable, so the nearest one wins and serialization prints the value that survived. The id changed by one in transit. Nothing in the function is wrong and nothing in the run reports anything.

    The second line is the part worth keeping. The literal in the comparison is parsed by the same rules as the payload, snaps to the same double, and the comparison passes. A check written inside the representation inherits the representation. Two checks that step outside it were run on the same value and both disagree with it: the parsed number is not a safe integer, and its string form is not the string that arrived.

    The timeline named the upstream service as the source and never recorded that the check was green. The prescription is one line long and it is not about being more careful. Run it, then read it. Reading tells you what the code is for and running tells you what it does, and across the five files in this lesson those two answers differed every time, with no diagnostic, no warning and no line in any log that would have brought anyone back to look.