Retro-driven development

https://thoughtbot.com/blog/retro-driven-development
Rob Whittaker in United Kingdom

Every session ends with a retro. This week, twenty-four commits out of about a hundred and forty started with that retro. Only a handful added anything new. I wasn’t building the system anymore. It was refactoring itself.

It is Week Four.

Tuesday: four commits before lunch

The 17th. Four refactor-from-retro commits before noon. Reusing API connections across commands instead of reconnecting each time. /morning filtering rules. Stale 1:1 prep dropped from the daily log. The system had been running for three weeks, and friction points had accumulated. I was working through them in fifteen-minute bursts between meetings.

By the end of the day, I had added eight more commits. An actionability check for /context. Top 7 priorities in the daily log. A self-management outcome for my Fusion goal. Retro after retro, feeding back into the commands.

Wednesday: picking sides

Wednesday ran hard. Nine refactor commits between meetings.

I read Sally Lait’s post on semantic calendar emoji and colours. I copied her system straight into /calendar:

  • 🦚 Peacock (default): 1:1s, ad-hoc work
  • 🫐 Blueberry: recurring group meetings
  • 🌿 Sage: pairing, workshops, active work
  • 🍌 Banana: internal socials, external community
  • ✏️ Graphite: transit, food

By evening, I’d refactored /evening to use Ruby instead of Python. It was a small religious war. I picked the side my team knows. The CLAUDE.md gained a preference note. This sort of thing accumulates.

Thursday: the Anytime problem

The Anytime list from Things hit 75,000 characters. That’s around 19,000 tokens. It triggered context compaction mid-session. I noticed overdue items slipping through the cracks.

I needed a filter. Not a prompt. A real script. I wrote bin/filter-anytime.

KEEP_FIELDS = %w[Title UUID Tags Area Project Deadline Notes]

items.each do |raw|
  status = raw[/^Status:\s*(.+)/, 1]
  next if status =~ /completed|canceled/

  if tags.include?("Waiting") && deadline_str
    deadline = Date.parse(deadline_str) rescue nil
    next if deadline && deadline >= today
  end

  puts raw
end

Fifty-five lines of Ruby. It runs before the agent sees the list. The filter runs on rules. It doesn’t guess. Overdue items stopped slipping.

Every session, the agent regenerates the filter. Not this time. I wrote real code. Guessing has limits.

Monday: a stretch of quiet time

A quiet Monday morning. Six refactor commits in one sitting. /calendar, /inbox, /weekly, /context. A stretch of uninterrupted time before the week’s meetings started.

That is when I realised the system had shifted. I wasn’t grinding through tasks. I was editing the system that edits my day. Maintenance, not task-grinding. The point of building a system is to make it fade into the background.

Tuesday: the cap

By the 24th, I noticed something else. My Anytime list kept growing. Each session, I added new tasks from retrospectives, meetings, and the inbox. The filter was treating the symptom. The disease was that the input exceeded the throughput.

I added a commitment cap.

Commitment cap: No more than 20 active next actions in
Things at any time across all areas (work and personal).
If /morning surfaces items that would exceed the cap,
flag it and ask what to defer before proceeding.

/morning now blocks the Top 7 until I’ve deferred enough items to sit under 20. The check is mechanical. I can’t talk it into running anyway.

What I learned

The dominant mode this week wasn’t invention. It was refactoring. Twenty-four commits out of about a hundred and forty say “from retro” or “from feedback.” The system improves by use, not by planning.

Retro-driven development. It works because the signal is cheap and the fix is small. Notice a friction point. Name it. In the next session, the command that caused the friction receives a line of new guidance. No meetings. No sprints. No planning.

The commitment cap came from one of those retros. So did bin/filter-anytime. So did Sally Lait’s colour conventions find their way into /calendar. Each started as an irritation, ended as a line in a command file, and changed how the next session ran.

Try it

Retros don’t need to be long. End each session with one. In the next session, fix what rubbed you the wrong way. The system is yours.

About thoughtbot

We've been helping engineering teams deliver exceptional products for over 20 years. Our designers, developers, and product managers work closely with teams to solve your toughest software challenges through collaborative design and development. Learn more about us.