Skip to content
Episode 152 · Twenty reminders for Ana

Reading code you didn't write (well enough to catch what matters)

Monday, 7:30. The arrival-reminder job the agent wrote on Friday was small, tested and demoed. Overnight, 4,100 guests arriving today got about 20 reminder emails each, and the email provider has paused StayHub's account.

You don't have to write code to read it for what matters: what it sends, charges, saves or deletes, and when.

30 min intermediateReading code: follow the data and the control flowSpotting side effects (writes, emails, payments)Checking edge cases and boundaries when reading codeAgent claims vs evidence ("done" is not proof)Idempotency
  1. Problem

    A small, green, agent-written job sends arrival reminders. It runs every hour. Overnight, 4,100 guests get about 20 reminder emails each.

  2. Why the simple system fails

    Running the code once in a demo shows one correct email. Tests check one call. Nobody asked what happens on the second run, because nobody read where the job is triggered and what it changes.

  3. Engineering concept

    Read code for consequences, not syntax: the trigger, the data in, the side effects, what runs again, and the edges. Trace it with concrete values instead of trusting anyone's explanation, including the agent's.

  4. Technology

    A five-step reading method, a list of patterns worth stopping on (send/charge/write/delete, loops, schedules, comparisons, null and undefined, dates), and asking the agent to explain then checking its explanation against a concrete trace.

  5. Trade-offs

    Reading everything line by line doesn't scale; reading nothing hands every decision to the agent. Read the risky parts deeply (side effects, money, data, permissions, schedules) and skim the rest.

01

The problem

2 min
Monday, 7:30. The agent's arrival-reminder job (2 files, 3 green tests, a demo that sent one email) went live Saturday.
  1. Omar · On-call SRE
    4,100 guests checking in today got about 20 reminders each. The email provider paused our account.
  2. Maya · Product manager
    The demo sent one email. I watched it.
  3. Lina · Senior engineer, your mentor
    You read the code; I'll only check your trace. Guess first.
02

The big idea

4 min
The second run
The big idea

Read code for consequences, not syntax: when does it run, what goes in, what can't be undone, and what happens the second time?

No single line was wrong. The bug was a combination: a schedule that repeats, a filter that keeps matching, and a side effect nothing remembers. A two-minute trace shows it; a demo never will.

03

How it works

5 min

You don't need to be a chef to spot that "add a tablespoon of salt" sits inside "repeat for each of the 12 layers". Reading every line from the top runs out your attention before the line that matters. Read in this order instead.

Five reading steps
  1. 1
    Find the trigger

    The trigger is what makes code run: a click, an API call, a schedule (). Here jobs.ts says "0 * * * *": every hour, 24 times a day.

  2. 2
    Follow the data in

    Say the filter as a sentence: "confirmed bookings checking in between now and 24 hours from now." Filters decide who gets hit.

  3. 3
    Circle the side effects

    A is a line that changes the world: sends, charges, writes, deletes. Here, one email.send per booking. It can't be unsent.

  4. 4
    Run it twice in your head

    16:00 run: Ana still matches, nothing marks her as reminded. Email again. Schedules, retries and double-clicks only misbehave on the second run.

  5. 5
    Probe the edges

    An edge case is a value at the boundary or missing. Trace each condition with true, false and missing, and ask which timezone every hour uses.

  6. 6
    Turn traces into tests

    Each trace becomes a test: run twice → one email; never-asked guest → no email. The agent's explanation is a claim; your trace is the evidence.

04

Watch a decision being made

4 min
Worked example

You check the agent's fix; Lina checks your trace

Trace Ana (booking 8812, check-in Tuesday 15:00, Lisbon) through the fixed reminder job, and decide whether to accept it.

  1. 1Read the filter as a sentence

    "Confirmed bookings, no reminder sent yet, check-in within 24 hours."

    ts
    where: {
      status: "confirmed",
      reminderSentAt: null,
      checkIn: { gte: now, lte: addHours(now, 24) },
    }

    Why: Saying a filter aloud catches wrong or missing conditions.

  2. 2Circle the side effects

    Two lines: email.send(...) and the update that sets reminderSentAt. Everything else only reads.

    Why: These are where a mistake reaches a guest; attention goes here first.

  3. 3Run Ana twice

    Monday 15:00: matches, email sent, marked. Monday 16:00: reminderSentAt is set, no match, no email.

    Why: The second run is where the original bug lived. Now it does nothing.

  4. 4Probe the edges, choose out loud

    Cancelled after the reminder: no more emails. Email sent but the update fails: one duplicate next hour. Accepted: better than a guest with no arrival instructions. Then: "Turn these traces into tests."

    Why: Some edges you fix, some you accept on purpose; the tests keep checking after the next change.

The whole thing in one line: Trigger, filter, two side effects, two runs, the edges: the fix is accepted with one rare duplicate chosen deliberately, without writing a line of code.
05

In real life

3 min
Reading vs running

Static reading (e.g. GitHub's pull request diff view)

  • See every path, even ones never run
  • Spot schedules, missing values, timezones
  • Only as good as your attention

Finds what demos never try.

Running it (e.g. browser DevTools, logs, a debugger)

  • See what really happens
  • Catches surprises in libraries
  • Only shows the cases you try

Proves the cases you think of.

06

What goes wrong

3 min
Reviewing by running it once

Schedules, retries and double-clicks misbehave on the second run.

Instead

Find the trigger; run it twice in your head, then in a test.

Accepting the agent's explanation

It can describe what such code usually does, not what this code does.

Instead

Trace one concrete case with true, false and missing, and compare.

Skipping timezones

Servers often run in UTC; hotels don't.

Instead

Ask which timezone every date and hour comparison uses.

07

Trade-offs

3 min
Reading ruleFits whenMain risk
Don't read; trust testsThrowaway prototypes, no side effectsMisses repetition, missing values, time
Read the risky parts deeplyDefault: sends, charges, writes, access, schedulesA bug in "boring" code
Read every linePayments, security, deletionAttention fades; doesn't scale
Friday, 10:00. What does "reviewing" mean for people who don't code?
  1. Karim · CTO
    Vibe coders shouldn't read code. That's what tests are for.
  2. Omar · On-call SRE
    Both incidents passed their tests. Everyone reads every line.
  3. Maya · Product manager
    Every line of a 300-line PR? I'd stop at line 40.
08

Your job as tech lead

4 min

Claude Code can write the code for this. You still own the decisions, and you have to know what good looks like to accept or reject its work.

Decisions you own
  • Which changes get a deep read: anything that sends, charges, writes, deletes, decides access or runs on a schedule.
  • What should happen when a side effect runs twice, and which rare failure you accept, out loud.
  • How a missing value is treated in conditions on consent, money or access (never asked means no), and which timezone time rules use.
Tell Claude Code
Explain sendArrivalReminders in apps/ops/src/jobs in plain English. Then list: where and how often it's triggered, which bookings it selects (as a sentence), every line that sends, charges, writes or deletes, what happens if it runs twice, and what happens if the email sends but the update fails. Use booking 8812 (Tuesday 15:00, Lisbon) through two hourly runs. Don't change code.
Check before you accept
  • Find the trigger yourself before reading anything else.
  • Circle every send, charge, write and delete; compare with the agent's list.
  • Your traces became tests: run twice → one email; missing opt-in → no email.
Red flags
  • A scheduled job with a side effect and nothing recording it already happened.
  • !== false or != null on consent, money or permission fields.
  • getHours() on server time for a hotel rule.
09

Check yourself

2 min
Q1/3Reasoning
The unfixed reminder job runs every hour and emails every confirmed booking with check-in in the next 24 hours. A guest's check-in is exactly 24 hours after a run. Counting that run, how many reminders does the guest get before check-in (at most)?
How confident are you?

Next: a pull request that says "fixed the flaky test, 2 files changed". The diff says something else.

Finished?

Mark it complete to update your skill map.

Go deeper (optional)You don't need this to understand the lesson. Open it when a project needs the details: Technical deep dive, Visual diagram, Analogy, Why it matters, Check your understanding, Practice ladder, Explain it yourself.
10

Technical deep dive

Trace Ana through the fix
Worked example

Read the reminder job and check the fix

Read the agent's reminder job with the five steps, trace Ana (booking 8812, check-in Tuesday 15:00, Lisbon) through the fix, and turn the trace into tests.

  1. 1Find the trigger

    Ask the agent: "Where is sendArrivalReminders scheduled?" Answer: jobs.ts, "0 * * * *". Every hour.

    Why: Everything else is read through this fact: whatever the job does, it does 24 times a day.

  2. 2Read the filter as a sentence

    "Confirmed bookings, no reminder sent yet, check-in between now and 24 hours from now."

    ts
    where: {
      status: "confirmed",
      reminderSentAt: null,
      checkIn: { gte: now, lte: addHours(now, 24) },
    }

    Why: Filters decide who gets the side effect. Saying them aloud catches wrong or missing conditions.

  3. 3Circle the side effects

    Two lines: email.send(...) and db.booking.update(... reminderSentAt ...). Everything else only reads.

    Why: These are the lines where a mistake reaches a guest. Your attention goes here first.

  4. 4Run Ana twice

    Monday 15:00: matches, email sent, marked. Monday 16:00: reminderSentAt is set, so no match, no email.

    Why: The second run is where the original bug lived. Now it does nothing, which is what we want.

  5. 5Probe the edges

    Cancelled after the reminder: no more emails (status). Email sent but the update fails: one duplicate next hour (accepted: better than a missing reminder). checkIn stored in UTC: compared with now in UTC, fine.

    Why: Every condition gets a concrete value on each side. Some edges you fix, some you consciously accept.

  6. 6Check the explanation, keep the trace

    Ask the agent to explain the job and list side effects; compare with your notes. Then: "Turn these four traces into tests."

    Why: Your trace is the evidence the explanation is checked against, and the tests keep checking it after the next change.

The whole thing in one line: You read the job for consequences (hourly trigger, filter, two side effects), traced a named guest through two runs and four edges, accepted one rare duplicate on purpose, and turned the trace into tests, all without writing code.
PatternLooks likeAsk
Side-effect verbssend, charge, refund, update, deleteTwice? Undo?
Repetitionfor, map, cron, retryHow many times?
Conditionsif, filter, ===, !==, >=Each side of the boundary?
Missing valuesnull, undefined, ?, ??What if it's not there?
Timenew Date(), getHours, addHoursWhich timezone?
Five patterns to stop on. You don't need the rest of the syntax.
11

Visual diagram

Click a component

The arrival-reminder job read with the five steps. The bug isn't in any single line; it's in the combination of the trigger and the side effect.

12

Analogy

  • Reading a recipe"Start the night before"Reading codeThe trigger
  • Reading a recipeThe ingredient listReading codeThe data going in
  • Reading a recipe"Bake": no going backReading codeSide effects
  • Reading a recipe"Repeat for each layer"Reading codeLoops and schedules
  • Reading a recipeNo butter? Hot oven?Reading codeEdge cases

Where the analogy breaks: a recipe is written for people, who fill in what was meant. Code does exactly what it says, even when nobody meant it.

13

Why it matters

Some bugs only show up when you read.

A job that runs every hour works perfectly the first time. The demo shows one nice email. The bug is on the second run, and the third, and the twentieth.

A filter like "not false" looks like "said yes". But it also lets through everyone who was never asked. Only trying a missing value shows it.

Ten minutes of reading a short function is the cheapest insurance on anything that emails, charges, saves or deletes.

14

Check your understanding

Q1/3Code
Which lines are side effects worth stopping on? (Select all that apply.)

Select all that apply.

How confident are you?
15

Practice ladder

Step 1 · Trace

Trace the late check-out fee

Debugging Warm-up

Trace this agent-written function for three guests and say what each is charged.

// Called when a guest checks out in the app
export async function checkout(booking: Booking, now: Date) {
  if (booking.lateCheckout && now.getHours() >= 11) {
    await payments.charge(booking.cardId, booking.hotel.lateFee);
  }
  await db.booking.update({ where: { id: booking.id }, data: { status: "checked_out" } });
}

The server runs in UTC. All three are at a Lisbon hotel (UTC+1 in summer), late fee 20 EUR, in July. 1. Ana, lateCheckout true, checks out at 11:30 Lisbon time. 2. Ben, lateCheckout false, checks out at 13:00 Lisbon time. 3. Chloé, lateCheckout true, double-taps "check out" at 12:30 Lisbon time.

Step 2 · Complete

Finish the reading notes

Code Practice

Lina's reading notes on the fixed reminder job have three gaps. Fill them in.

Trigger:      hourly ("0 * * * *")
Data in:      confirmed, reminderSentAt is null, check-in within 24 h
Side effects: (1) ___  and  (2) ___
Run twice:    second run finds reminderSentAt set -> no email
Edge:         email sent, then the update fails -> (3) ___
Step 3 · Solve

Read the deposit refund job

Debugging Practice

Read this nightly job with the five steps and list what you'd ask the agent to change.

// jobs.ts: "0 2 * * *" (02:00 UTC daily)
export async function refundDeposits() {
  const stays = await db.booking.findMany({
    where: { status: "checked_out", depositRefunded: false },
  });
  for (const s of stays) {
    if (s.damageReported) continue;
    await payments.refund(s.depositId);
    await db.booking.update({ where: { id: s.id }, data: { depositRefunded: true } });
  }
}
Step 4 · Decide

Which PRs get a deep read?

Decision Stretch

This week's agent PRs:

  1. New colours and spacing on the housekeeping board.
  2. A job that deletes guest chat messages older than 2 years.
  3. A new filter on the support tool's search page.

Decide how deeply to read each, and write your rule as "We chose X over Y because…, accepting the risk of…".

16

Explain it yourself

A teammate says: "I can't code, so reading the agent's code is pointless. I just run it and see." Explain what running once misses and how they can read usefully without writing code.

0 words