A Web Dev Builds a Backend (The Comfort Zone)
After eight days of iOS and Swift and Metal, Fastify and Prisma feel like home. Plus the monorepo decision I made for the AI agents, not for me.
Eight days in. The game ran on an iPhone. TestFlight was shipping to my wife and Or. The Metal rewrite was humming at 60fps. CLAUDE.md was getting fleshed out. I had a reasonable product plan from the planning day. And I needed a backend.
Not because the game needed one yet. It was still single-player, local state, no accounts, no leaderboards. But the planning day had made clear that the trajectory required a backend. Leaderboards, run submissions, economy, identity, analytics. All of those need a server. And servers take time to build well, so starting now (while the game was in its messiest state) was cheaper than starting later (when I'd have twenty features waiting on a missing API).
This post is about the three days where I built the backend scaffold. It's the shortest stretch of the series because, for the first time since day 1, I was working in my comfort zone.
The bug bash
Day 8 opened with a small but meaningful commit:
Fix micro bugs batch (AYA-87, 88, 89, 90, 91, 93) — 6 bugs in one commit
Six bugs in one commit. Not six major bugs. Six small ones that had accumulated over the previous week and were making the game feel unpolished. Sticky particles that weren't clearing properly. A milestone label that got cut off on certain screen widths. The hurry-up banner getting stuck on screen after the chase camera backed off. A game-over flash, a single visible frame of white screen during the transition. The pause panel rendering slightly off-center. The joystick retaining its last direction for one frame after the player lifted their finger.
None of these are bugs I'd prioritize on day 8 of a brand-new project. All of them are bugs I noticed while playing the game and added to Linear as I noticed them. No formal QA process. No beta testers reporting issues. Just me playing the game and going "wait, that's not right."
That's the texture of solo dev. You're the QA team. You find bugs by playing your own game. You batch the small ones because each one is a five-minute fix and splitting them into six separate commits is overkill for a solo project. The bug-bash commit is a pattern you'll see again in this series.
The backend begins
At 23:26 on day 8 (I was in Andorra on a ski trip at this point, more on that below), the first backend commit landed:
Add backend server scaffold, monorepo restructure, and iOS client prerequisites — 238 files
Two hundred thirty-eight files is a lot, but most of them were boilerplate: package.json setups for the new workspaces, TypeScript configs, Prisma schema, Docker setup, and a restructuring of the existing iOS code into apps/ios/ so the new apps/server/ could live alongside it. The actual server code was modest. A Fastify instance. A handful of stub routes. Prisma schema with three or four tables. An env file. Dockerfile.
I am a web dev at my day job. I've been writing Node and TypeScript for years. Fastify + Prisma + PostgreSQL is muscle memory for me. This is the exact stack we use at Smartbull for the production fintech system I'm responsible for day-to-day. After eight days of unfamiliar territory (Swift, Metal, Xcode, Obj-C++ bridges, App Store Connect), dropping into a Fastify scaffold felt like taking off a pair of uncomfortable shoes.
I've had this come up in every conversation with other devs about Claude Code: your comfort zone is still your comfort zone, and the AI doesn't change that. What changes is how far you can go outside it. On days one through seven I was outside my zone entirely. On day 8 I was back home. The tool was still there, still useful, but the feeling was different. Less wonder, more velocity.
The workflow itself was identical: agent-assisted research, brainstorm, plan, implementation. Same steps, same methodology. The domain was just familiar. "A breeze" is the honest description. I did in three days of evenings and ski-break sessions what would normally be two weeks of after-hours grinding on a side project.
The monorepo decision
One thing deserves a callout. The 238-file commit also restructured the repo as a monorepo. Before this day, the project was just an ios/ folder at the root. After this day, it was apps/ios/, apps/server/, and (soon to come) packages/catalog/ for code shared between iOS and the server.
The reason I went monorepo wasn't the standard monorepo argument. I wasn't trying to simplify cross-package atomic commits or shared CI or whatever the usual rationale is. I went monorepo for a specific reason that I've never seen mentioned in the "monorepo vs polyrepo" discourse:
Monorepos mean agents have easy access to all the context.
When Claude Code is asked about a server endpoint, it can read the iOS client that consumes it. When Claude is asked about the iOS client, it can read the server contract it talks to. When I'm debugging a sync issue, Claude can see both sides of the wire in one grep. In a multi-repo setup, Claude has to guess at the sibling repos, or I have to manually explain "this is the iOS side, the server lives in a different repo over there." Guessing loses context. Explaining burns tokens.
In a monorepo, everything is discoverable in one git grep.
The obvious caveat: with a monorepo, everything is discoverable, including stuff you don't want Claude crawling on every trivial task. If I ask Claude to fix a typo in a button label, I don't want it reading the entire server codebase first. The solution is a good CLAUDE.md that tells new sessions what to skip for small tasks. CLAUDE.md says things like "the iOS app is at apps/ios/, the server is at apps/server/, and for any change scoped to one of these you probably don't need to read the others." With that guidance, Claude stays focused.
Monorepo plus disciplined CLAUDE.md equals an agent-friendly project structure. I'm applying the same pattern at my day job now. It's the single biggest structural decision I've made about tooling in the last year.
Review rounds
Days nine and ten (2026-03-25 and 2026-03-26) were two rounds of review fixes on the backend scaffold. The commit messages tell the story:
Fix critical review issues: atomicity, idempotency, type safety, and missing packages Fix second review findings: reward atomicity, iOS API contract, and type safety
Two rounds. Not because I was sloppy on the first pass, but because even in my comfort zone, Claude didn't catch everything in its initial implementation, and I didn't catch everything in my first review of Claude's implementation.
The issues were the classic backend concerns:
- Atomicity: the initial reward grant logic wrote to the balance table and the transaction log in separate queries, not wrapped in a transaction. If the process crashed between the two, you'd have a ledger out of sync with the balance.
- Idempotency: the run submission endpoint didn't have a proper idempotency key. Re-sending the same run would grant rewards twice.
- Type safety: a couple of spots where
anyhad slipped into the Fastify handler signatures. - Reward atomicity (second round): related to the first issue but in a different code path. The reward calculation was separated from the reward application, and the split opened a race condition window.
- iOS API contract: the Swift client expected a different response shape than the server was returning. The kind of thing you only catch when you actually call the API from the client.
None of these are bugs that would have been caught by "just run the tests" because there weren't proper tests yet. (That comes at the end of this stretch, when integration tests with a real Postgres land in PR #17.) They were caught by eyeballing the code, once in a first-pass review and once in a second-pass review.
Claude didn't catch any of these. There's a narrative about AI-assisted development that goes "you ship, Claude reviews, it's perfect." My experience is more like "Claude produces a first draft that's 80% right, I review it and catch the things it missed, we fix them, I review again, I catch a few more, we fix those." The review rounds are part of the workflow, not an accident.
Even in your comfort zone, review Claude's code the way you'd review a strong junior's code. Not because you don't trust it. Because the second pair of eyes is where the value is, and if the second pair of eyes is also yours, you still need to actually look.
Shipping from Andorra
I have to mention the context for this stretch because it explains the pace.
From 2026-03-23 to 2026-03-28, I was on a ski trip to Andorra. Empty mountains (off-peak timing), good prices, gorgeous views. It was planned months ago, long before this project existed, and I went on the trip anyway because why wouldn't I. The pattern that emerged, and held for the entire trip:
- Mornings: skiing
- Afternoons: break at the hotel, Claude Code on the laptop, push a PR or two
- Dinner: a couple of hours off
- Evenings until midnight: more Claude Code
Six days of this. The backend scaffold, the first round of review fixes, the second round, the Docker build, the GCP deployment (coming up in the next post), the auto-deploy pipeline. All of it happened from a hotel room in the mountains.
The romantic indie dev fantasy is a garage in Silicon Valley. The actual indie dev fantasy is a ski lodge with fast Wi-Fi. I'm not kidding about this. The mornings of physical activity reset my brain completely. I'd come back from the mountain at 13:00, grab lunch, and hit a coding session with a clarity I don't normally have in the evenings. And the evenings were also productive because I was in a new environment with no day-job distractions and no familiar procrastination patterns.
If you're thinking about a side project and you have any flexibility about when and where to work on it, consider this. Don't wait for a free weekend. Book a trip somewhere with good internet and structure your days around it. The constraint of "I'm here specifically for skiing and coding" focuses you in a way that "I'll get to it when I have time at home" never will.
This is post 6 of 18 in a series about building Geo Climber with Claude Code. The backend foundation landed in three days, all from a ski lodge in Andorra. Join the Discord and download Geo Climber on the App Store.