Case study · Own product · Since 2024

SailMOB: a solo game, built like an org.

A browser sailing game with real wind physics, raced head-to-head against an AI fleet. One person built it, so the process had to do the work a team usually does.

FrontendReact 19 · Canvas · Zustand
BackendSupabase · Stripe · Vercel
PlatformWeb and iOS · Capacitor
DisciplineTDD · seeded determinism

SailMOB races real sailboats against real wind. You trim, you tack, you cover your rival, and AI bots sail the same rules you do. The hard part was never a single feature. It was keeping a physics simulation, a rules engine, a payment system, and a native iOS build honest while one person moved between all of them.

So I stopped treating it as a side project and started treating the repo like a small company. What follows is the development approach: the order I built it in, how I made nature the spec, who played expert and who played tester, and the guardrails that let one person ship at a team's cadence.

Physics first, then the boat, then the fleet

The build order was deliberate: wind and water first, then one boat that sails true in that wind, then a fleet of bots sailing beside it, then the rules that govern their meetings, then the tactics that exploit those rules. Each layer depends only on the one below it, so each layer could be finished, tested, and trusted before the next went on top. Tacking mechanics never had to be debugged on top of broken wind math, and bot tactics never had to be tuned against a rules engine that might itself be wrong.

That ordering is the biggest thing this project taught me about complex problems. Most problems that feel too big are really a stack of dependencies tangled into one knot. Find the dependency spine, build the load-bearing layer first, and make it provably true before anything stands on it. Every layer you finish becomes solid ground instead of open water.

Nature is the spec

For the physics there was no spec to write, because nature already wrote it. I researched how wind actually behaves and how real hulls accelerate, and encoded boat speed as polar curves: the charts of expected speed at every wind angle that real racers navigate by. The same polar table that tells a bot its fastest angle to sail is the one the physics engine honors, so the AI and the world can never disagree about what a boat can do. Wind shadow cast by a rival's sail, the lee-bow effect, puffs and shifts: all modeled, because a sailor would notice their absence immediately.

The lesson: simulation work is research work. You do not invent behavior and tune it until it feels right. You find the math that describes the real thing and port it, and the "feel" takes care of itself.

My own subject-matter expert

I spent twenty years as a sailing instructor, so on this project the domain expert and the builder are the same person. That collapses the most expensive loop in software: no requirements telephone, no waiting a week to ask what "mark-room" implies in an edge case. It also set the quality bar for the rules engine, written directly into its tests: does the give-way boat actually keep clear, and does the engine rule the encounter the way a sailor would expect?

A test fleet, ages 8 to 15

Experts come with a known trap: they cannot see what beginners trip on. So the other half of my QA org is my nieces and nephews, ages 8 to 15, all learning to sail in real boats. They are exactly the audience a sailing-school product has to serve, and they deliver feedback with a candor no focus group can match. A kid who just learned what starboard tack means and still gets confused by a penalty call has not found a UX nit. They have found a bug in how the game teaches. Between the instructor and the juniors, every change gets judged twice: is it true, and does it teach.

A simulator instead of a QA team

You cannot unit-test "the race felt fair" from screenshots, so the tests aim lower and truer. Everything in the game that involves chance runs through a seeded random generator, which makes every race replayable: same seed, same race, every time. On top of that determinism sits a command-line race simulator that runs whole fleets headlessly and writes the outcomes to versioned baselines. When the physics change, the baseline diff is the review. I regenerate the baseline in the same change, so the diff shows exactly what the new physics altered, and nothing changes by accident.

Refinement is driven by metrics that point at causes. One example: the foul analysis counts rule infringements by distance from the nearest mark. Fouls inside a crowded mark rounding are just racing. Fouls in open water mean a bot's avoidance logic regressed. The open-water foul count, broken down by race leg and by which tacks the two boats were on, tells me exactly which encounter geometry to go fix.

The Rules Lab

The actual numbered Racing Rules of Sailing are modeled: right of way, mark-room, penalties, exoneration. To refine them I wrote the canonical racing encounters as plain data, with two or three boats staged in a precise geometry: a port boat crossing a starboard boat, an overlap formed at the zone, a penalty turn spun in traffic.

That one scenario library is consumed twice. A headless feel runner replays every encounter and grades the whole stack against the sailor's-eye bar above. And the in-game Rules Lab spawns those same encounters for a player to watch and sail, which turns the test harness into a teaching tool. The standard it is held to comes from the sport itself: official rules cases should come out the way the judges ruled them. When the test suite and the curriculum are the same artifact, every refinement pays twice.

Divide the codebase like a crew

A sailing crew has fixed roles: someone on the helm, someone on the sails, someone on the money. I gave the codebase the same structure, and the crew is AI. Work is routed to three AI coding agents, each owning a fixed territory of the code, and I direct and review them the way you would run a small team. An agent only touches its own surface, so a physics change cannot casually reach into billing, and a UI tweak cannot quietly alter a racing rule.

Helmsman

Owns physics, bot AI, the rules engine, and the simulation tests. Everything that decides how a boat moves and who was in the right.

Coxswain

Owns the interface, the canvas render layer, the landing site, and the brand. Every UI change ships with a visual check before it merges.

Purser

Owns Stripe, subscriptions, the paywall, and in-app purchases. The one surface where a silent bug costs real money, kept walled off on purpose.

Make the rules executable, not aspirational

Every project has a document of good intentions that nobody reads. SailMOB has a Constitution instead, and it runs as a command. Before I touch code, a single check verifies the invariants that matter: the right accounts are active, the branch is fresh, nothing load-bearing has drifted. If it comes back red, that gets fixed before anything else. The same idea shows up as hard limits: file-size budgets fail the build, and a pre-commit guard refuses commits straight onto the main branch. None of this is willpower. It is the machine enforcing the standard so I don't have to remember it at 11pm.

Ship, track, move

Solo velocity dies two ways: half-finished branches pile up, or fear of breaking things slows everything down. The workflow answers both. A change is done when the behavior lands. Anything deferred becomes a tracked issue, never a dangling local branch. Work that can't ship today is saved as a draft pull request, so nothing lives only on my laptop. Open PRs are capped, and the cap is drained, not raised.

One place to look when it breaks

When something fails in production, the worst outcome is a scavenger hunt across five dashboards. SailMOB has one front door. Errors surface in a single incident tool first, and every other signal, the analytics session, the in-game state snapshot at the moment of failure, links back to that one record through a shared id. Diagnosis starts in one place and fans out, instead of the reverse.

What carries over to client work

SailMOB is a game, but almost none of this is about games. Decompose by dependency and build the load-bearing layer first. Research the real system instead of inventing behavior. Encode expert judgment into tests, then check the expert against real beginners. Automate the checks a team would do by hand, and pick metrics that point at the exact thing to fix, the way an open-water foul count does. That is the engineering I bring to a client's systems, where the stakes are usually higher than a sailing race, which is precisely why the guardrails matter more.

If you are building something custom and want it to hold up as it grows, that is the work: custom apps and APIs and AI and automation wired into how your business actually runs.

Next step

Want this kind of rigor on your build?

Play SailMOB to see the result, then let's talk about bringing the same discipline to your product. I map the work, find what's worth automating, and build the system that holds up.