Learning Distributed Systems by Building My Own Budget App
Why I'm building a household budget that syncs across a desktop, a laptop and a VPS, and what this series will cover.
In short: I'm building a real application (a budget that stays in sync across a desktop, a laptop and a cloud server) to learn distributed systems properly, with an AI assistant as my coach. This series documents what I built, what broke, what I measured, and where the ideas apply in production systems.
Most distributed-systems material starts with a diagram of five identical servers and a key-value store. I read plenty of it, and I could recite the words: vector clocks, anti-entropy, consensus, idempotency. What I couldn't do was use them, because nothing in those diagrams was something I cared about.
So I'm building something I do care about: a budget for my own household, written in Go with HTMX and SQLite, that stays in sync across the three machines I actually use.
| Machine | Its job |
|---|---|
| Desktop PC | Where I import bank statements and plan |
| Laptop | The same budget when I'm out, including offline |
| VPS | Always on: the hub the other two sync through |
That setup forces every classic problem into the open. The laptop goes offline and comes back with changes. Two machines edit the same thing. A sync request gets retried. The VPS can't reach my home machines, because they're behind a router. None of these are hypothetical: they're the week-to-week reality of the project.
What I've built so far
Week 1: the unglamorous foundation. Configuration from environment variables, password hashing and sessions, structured JSON logs with request IDs, and a Docker image with health checks. Five boring things, each of which broke in an instructive way.
Week 2: a replication engine. Vector clocks to track what each machine has seen, a sync API that tolerates retries, Merkle-style bucket summaries so machines exchange only what differs, deterministic conflict resolution, and a background loop that keeps nodes converging on their own. Two real processes (and later a three-node Docker ring) converge after independent and conflicting writes, and a scripted demo proves it.
The honest part
By the end of Week 2 the engine worked, and I was struggling anyway. It moved placeholder
data (item_id: "rent", value: 1200) and wasn't connected to the budget page I actually
use. Every concept stayed abstract. So I changed course: from Week 3 on, the engine carries
real budget data. Transactions, bank CSV imports, monthly limits. Every lesson gets
explained through a concrete money problem. That turned out to be the most useful decision
in the whole project, and one article in this series is about why.
The series
- The Boring First Week: What Broke While Setting Up a Small Go Service
- Vector Clocks, Explained With a Budget
- Retries Are Normal: Building an Idempotent Sync API
- How Two Computers Find Their Differences Without Sending Everything
- When Two Edits Collide: Deterministic Last-Write-Wins
- Go Gotchas I Hit Building a Replicated Service
- My Distributed Systems Project Felt Impossible Until I Made It About Money
- I Learned Distributed Systems With an AI Coach. Here's Where It Was Wrong.
- TLA+ Found Two Bugs in My Tested Sync Protocol on Its First Run
Each article is built around something that actually went wrong, because that's where I learned the most. The code is Go, but the ideas carry over to any language.
How each article is written
- An "In short" summary at the top, for anyone who wants the point in thirty seconds.
- The technical story, with real code, real bugs and real measurements.
- "Where this shows up in real systems", connecting the idea to production: payments, databases, mobile and edge apps, team practices.
A note on AI
I'm building this with an AI assistant as coach and, at times, pair programmer. Mostly it explains and reviews while I write the code; for some stretches it wrote code while I reviewed. I verify everything against tests and measurements, and part 8 is about the times it was confidently wrong and how I caught it.
I write about building reliable software in Go, from the code up. If you're working on similar problems, I'd like to hear from you: get in touch or connect with me on LinkedIn.