# The wrong unit

Remember when I [told you all I'd swim in one lane](https://blog.codemanship.dev/rant-code)? Well... I'm swimming in two to six lanes now. And I can report that my deploy paralysis is gone, and that my mental fatigue doesn't kick in meaningfully until the end of the day.

Which means either I've learned nothing at all, or something changed in between.

For those who missed it (or didn't want to follow a link on the first sentence of an article): I'd had five projects going at once, my nervous system was fried, I was saying yes to everything without even looking, and it took me about a month to dig myself out of the PRs that came back. I concluded that we have to do less. Well, now I can juggle two to six tasks at once.

The models might have gotten marginally better, but that's not what changed. I was right that I had to do less, but I had the wrong unit. The problem was how much I was asking the model to do in each stride. And by lowering that amount I can keep more things in parallel actually moving.

Before any of the specifics, the thing all of it is built on.

## So what is a stride?

A stride is the smallest thing I can ask for that gets me in the right direction from green (passing tests) to green. I do have a final destination in mind, and I share that with the model, but a stride is really the smallest directionally correct step I can take. In practice that's usually one behaviour, and the test that drove it.

Of course, I'm building on the shoulders of giants here, and [GeePaw Hill's](https://www.geepawhill.org/) [Many More Much Smaller Steps](https://www.geepawhill.org/series/many-more-much-smaller-steps/) is what inspired this. Stop here and go read that, I'll wait, and it's worth it! The final trigger was a conversation we had on [A Junior, A Senior and I](https://open.spotify.com/episode/4GLP0naEVVLFZ6SzkISA17?si=ui_6_arTRs-hoIIv6qmK8A) with Tim and Paul.

I'd rather bound a stride by time, the number I care about is the minutes between meaningful interactions. But the model can't feel time. If I ask it for "about ten minutes of work", it hands me a feature. It can count lines though, and 40 is a size it can both hold and check itself against.

Here's the shape of one. Say I want a report to leave a blank rather than a zero when there's nothing to measure yet. The test comes first:

```ruby
test "reports no median for a step where nothing was paid" do
  report = EffectivenessReport.new(rows: [step_with_no_payments])

  assert_nil report.rows.first[:median_days_to_payment]
end
```

And then the change that makes it pass:

```ruby
- median_days_to_payment: row["median_days_to_payment"].to_i
+ median_days_to_payment: row["median_days_to_payment"]&.to_i
```

You'll notice that this stride is quite small. Not all of them are like that, but I want to stress that a stride is not necessarily a feature, it can be as small as a three line change. Most are around 40 lines, but I don't have many 200+ line diffs anymore. One stride is one commit.

Once the stride is done, it's up to me to decide where we're going next (although the word generator sure likes to make "suggestions").

## But how do you run six at once?

One stride at a time is easy when you're in one lane. The reason I can be in six is that opening a lane costs me nothing.

`claude-dev task <task-name>` opens a tab, creates the worktrees if they don't exist, and lays out four panes: a claude session on the left, resumed if one already exists, and shells on the two worktrees and their shared parent on the right. It's tmux inside Ghostty, and you can probably reproduce it with other tools. That's the whole thing — I'm not really in need of a helicopter view.

I'll usually have two to six of those running. One of them is my actual priority. The others are things that need less attention from me: migrating legacy code, migrating specs. Sometimes something small someone asked for, a graph, some quality of life thing. When a new thing pops up, I reach for `claude-dev task <new-thing>` and pick the first stride.

Once a stride is done I get a notification, and I open the tab to be greeted by the diff. On the whole I'm interacting meaningfully with each task every 5 to 10 minutes, and each interaction lasts anywhere from 30 seconds to 5 minutes.

I'm steering the system, and yes, that's context switching all day long. The difference is what a context costs now: 40 lines and the test that drove them, instead of a whole branch I have to rebuild in my head.

## The part I won't hand off

This is the part I don't want to hand off. The model is bad at it.

When a stride lands, two things arrive with it: the diff, and a suggestion for what to do next.

The review pane gives me the files that changed, a map of what else in the codebase references them, and then the diff itself. That's enough to see whether it's doing what I asked, and to the standard I expect. Since it's small, I can go through it quite easily and fast, and it doesn't take much out of me. From there I either approve, or I leave a note:

```diff
   def overdue?
-    due_on < Date.current
+    due_on < Date.current && !paid?
   end
 
-  def formatted_total
-    "%.2f" % total
-  end
+  def formatted_total = format("%.2f", total)
```

Scope creep like this is the most common thing I have to push back on. The note I'd leave, in the review pane:

> the formatting change is unrelated, revert it and keep the stride to the one behaviour

Once I close the review pane, either I tell it I left a note, or I look at suggested next strides. About two times out of three I pick one of the suggestions, but not always the "recommended". Strides are small, so if they're directionally correct that's usually good enough. If I'm not convinced I tell it to do a different stride.

## Throwing the conversation away

While I could deploy most strides as I go (they go *or should go* from green to green), ten minutes of CI per PR is enough to make me batch. So in order to deploy I bundle two to five strides together and create a PR at that point to trigger the CI and merge if green is confirmed.

One problem I had with this approach is that my context got bigger and bigger, and corrupted with unimportant artifacts (pushes, side conversations, etc). One thing that unblocked this is the introduction of cycles. A cycle is just that bundle: two to five strides, one PR, one merge.

Once a cycle is done, I have a `/cycle` command that will:

*   run `/handoff`: gotchas and lessons go to memory files, progress goes to the plan, anything a reviewer needs goes into the PR description. Then it writes a handoff. Where this task is going, how I'll know it's done, what's green and what's pushed, and one next stride.
    
*   close the claude session
    
*   and run `claude-dev cycle` which starts a new claude conversation that reads the handoff, gives me a one line status and the next stride it proposes, and waits.
    

That buys me tokens, and it flushes two sets of temporary memory: the session's, and mine.

## Some numbers, because you'll ask

The numbers I care about are the small ones: the median commit adds 59 lines, tests included. While I ask for 40, the model does overshoot a little. Nine out of ten are under 211. These are diffs I can parse easily, and correct on the fly.

Volume and PR count are probably the least interesting ways to measure "faster"... So here they are anyway.

Since the 1st of June, across both repos, I've made just under a thousand commits, over the 41 days I actually worked (the rest was holiday). In the same period 354 of my pull requests were merged, and at Recovr merging means shipping. So call it 24 commits and 8 deploys on a working day.

The number I'd most like to give you is the one I don't have: bugs in production. My honest read is that they're stable, maybe lower. That's a gut feeling and you should hold it against me.

With 8 merges a day, it's rare that I have a branch open for more than a couple of hours. That's the closest I've ever been to trunk based development, with all its benefits (low conflicts, code exercised sooner, etc.).

Which invites the obvious question: who reviews eight PRs a day? After the fact, mostly nobody. We use [Ship/Show/Ask](https://martinfowler.com/articles/ship-show-ask.html), and this way of working has taken full advantage of it. I review every line of code as I go (remember strides are small now) and in a trusting team formal PR reviews end up being quality theatre anyway. Ask is still there for changes that deserve another set of eyes.

And if you read all of this as "way more work", I've written this badly. I'm shipping sooner, not really faster.

## If you want to try this

I'm not going to tell you to copy my setup. The tmux part is incidental and you'll want your own anyway. But if I had to rebuild this on a new machine tomorrow, there are five things I'd want back, roughly in this order.

*   **Small steps, as an actual rule.** For me that's a skill file the model loads at the start of every session: what a stride is, how big it's allowed to be, and that it stops after each one. Everything else here is plumbing. This is the part that does the work.
    

*   **One command to start or resume a piece of work.** Mine creates the worktrees, opens the panes, and either starts a session or picks up the one that was already there. Not having to assemble that by hand is most of the difference between running one task and running six.
    

*   **A handoff, and a way to cut the session.** Where this is going, what's green, what's pushed, and the one next step. Then throw the conversation away and start the next one from that note.
    

*   **The diff, in front of you, automatically.** Not "go and look if you remember to". It shows up when the step is done, with the files that changed, what else references them, and the code.
    

*   **A way to clean up.** When a task is done, something that removes the worktrees and closes the window, and refuses if anything in there is uncommitted.
    

What all of it really buys me is permission to forget. Whatever mattered is written down by the time the session ends, so I don't have to carry it into the next one.

Last time I said the answer was fewer lanes. It wasn't, it was shorter strokes.
