Compare two approaches with branches
The validation layer is the last piece, and you’re of two minds about it. You could lean on a schema library - declarative, batteries included, a dependency. Or you could hand-roll plain guard functions - no dependency, more code, total control. On paper it’s a wash, and you’ve learned not to trust on paper. You want to see both, in your actual codebase, against your actual handlers, and then decide.
The wrong way to do this is the obvious one: ask the agent to build it with the library, then say “now also try it the other way” in the same conversation. Do that and the window now holds both approaches at once, the agent starts blending them - guard functions that suspiciously resemble schema calls - and you can no longer tell which idea is which. Comparing two designs in one thread destroys the very thing you’re trying to compare.
What you want is a branch: one clean starting point, two independent lines of work growing out of it.
Branching off a checkpoint
Section titled “Branching off a checkpoint”The checkpoints from the last lesson let you restore or summarize an earlier point in the current session, but they are not a replacement for a durable branch. To preserve the original conversation, use /branch or claude --continue --fork-session; to compare code, use Git branches from a shared base commit. Start by marking the point before either approach:
> [Esc][Esc] ❯ "the handlers are ready, now add validation" ← shared starting point "add zod schemas for each route" (the library approach grew here)
Restore: code + conversation
> implement the validation as plain guard functions instead - no library, explicit checks in each handlerThe first approach isn’t destroyed by this; its checkpoints still hang off the tree, and you can rewind back onto that line to look at it again. For a quick “which feels right,” that in-session branching is enough.
But you’re going to want these two side by side for more than a minute - to diff them, sleep on it, maybe show a teammate - and that’s past what checkpoints are for. Create both Git branches from the shared base, before either approach is committed:
# mark the shared starting point first> !git add . && git commit -m "handlers ready"> !git branch validation-base
# approach A> !git checkout -b validation-zod validation-base> add zod schemas for each route> !git add . && git commit -m "validation via zod"
# approach B starts from the same base, not from approach A> !git checkout -b validation-handrolled validation-base> reimplement validation as plain guard functions, no library> !git add . && git commit -m "validation via guard functions"
[builds approach B on its own branch, from the identical starting point]
# now compare for real, not from memory> !git diff validation-zod validation-handrolled -- src/validationNow the decision is concrete. You’re reading an actual diff - lines added, dependency added, the shape of each handler under each scheme - instead of arguing two half-remembered designs in your head.
The durability line
Section titled “The durability line”The thing to internalize here is where each kind of branch lives, because it determines how long it survives. Checkpoints are fast, session-local undo; they do not undo Git commits, branch changes, Bash side effects, or external changes. /branch and --fork-session preserve conversation state, while Git branches preserve code. Use the right primitive for the thing you need to keep.
And hold the line on one conversation per approach. The whole reason this works is that each branch grew in its own focused context, uncontaminated by the other. Collapse them back into one thread to “save time” and you’re right back to guard functions that think they’re schemas.
Naming the cost
Section titled “Naming the cost”There’s a quieter payoff under all this. When getting back is cheap, exploration becomes cheap - you can try an approach, diff it, and throw it away without the nagging fear that trying B means losing A. That fear is exactly what pushes people to settle for the first thing that compiles. Remove it and you’ll explore more, and ship better code for it.
You pick the hand-rolled version, delete the other branch, and merge. The feature’s done. But it’s now Thursday afternoon, this session has been alive since Monday, and the agent has started feeling… slower. Vaguer. You’re about to learn that the right response to “the agent’s getting dumb” isn’t to restart on faith - it’s to look at what’s actually in its memory.