A two person engineering playbook: which big company practices (code review, CI/CD, runbooks, ADRs) earn their keep at n=2, and the process theater to cut fast.
For four years the entire backend of a product we shipped to real paying users lived in the heads of two people, and one of them was on a plane over the Atlantic the night it fell over. That night taught me more about engineering process than any of my years inside bigger orgs did.
Most engineering advice is written for teams that look nothing like mine. When you're two people, the whole genre of "scaling your org" reads like a diet plan handed to someone who forgot to eat. Sprint ceremonies, RFC review boards, on-call rotations with follow-the-sun coverage: beautiful machinery, built to move a hundred people in roughly the same direction. Point it at two engineers and it just adds friction where there was none.
The opposite reaction is worse. The temptation at n=2 is to throw out all of it and call the mess "moving fast." I've done that too. It feels great for about four months, and then you're bisecting a production outage at 2am with no logs, no runbook, and a data model that made sense to exactly one person on exactly one afternoon. The real skill isn't process or no-process. It's knowing which big-company practices still earn their keep when the whole company fits in one Slack DM, and which are pure coordination tax you're paying for a crowd that doesn't exist.
This is the two-person engineering playbook I wish someone had handed me before that flight: a practical, opinionated guide to running a tiny team without either drowning in ceremony or drowning in chaos.
At twenty people, your bottleneck is communication. Most of the process in a large org exists to fight the O(n²) explosion of "who needs to know what." Standups, design docs, ticket hygiene, approval chains: all of it is there so that person 14 doesn't quietly undo person 3's work.
At two people, that entire class of problem mostly vanishes. My co-founder and I can sync the full state of the system in a ten-minute call. Nobody is stepping on anybody. So the failures move somewhere else:
So the goal of a two-person playbook isn't to coordinate a crowd. It's to defend against your own future amnesia and a bus factor of one. Every practice below is judged on exactly that. If a practice only earns its keep by moving information between many humans, it's dead weight at your scale. If it makes the system more legible, more automated, or more recoverable, it survives.
Back to that flight. We had a Firestore data model where one collection did too many jobs — user profiles, session state, and a denormalized activity feed all crammed into documents that were quietly growing past what a single read should carry. My co-founder had built it; I understood maybe 70% of it. It worked fine at a few hundred users.
The night it broke, we'd crossed some threshold and a hot document was getting hammered on every screen load. Reads spiked, latency followed, and the app started timing out on login. He was mid-air with no wifi. I opened the Firebase console and realized I couldn't confidently reason about the blast radius of any change I made, because the why behind that schema lived in a head that was currently over Greenland.
I got it limping by fronting the hot reads with a cache and cutting the per-screen read count from around 40 to 3 — but it took me four hours to do something that should have taken forty minutes, entirely because nothing was written down. The fix was easy. Understanding the system I was allowed to break was the hard part.
We didn't need Jira. We needed one page explaining why that collection was shaped that way, and a rollback command I trusted. That's the whole thesis of this post: at two people you are not defending against each other, you're defending against the version of you that has to operate the other person's brain at 2am without them.
Here's the test I run before adopting any engineering practice: does this pay off even if the team never grows past two?
If a ritual only makes sense because it lubricates communication between many people, it's theater at our scale. If it makes the system more legible, more automated, or more recoverable regardless of headcount, it's leverage. That single distinction — leverage versus theater — is what separates a lean two-person team from both a chaotic one and a bureaucratic one.
| Practice | Why big cos do it | Pays off at n=2? |
|---|---|---|
| Standup meeting | Sync many people daily | No — we already know |
| Code review | Catch bugs, spread knowledge | Yes — catches my bugs |
| Sprint planning poker | Estimate across a team | No — pure ceremony |
| CI on every push | Gate a shared main branch | Yes — it's a free second engineer |
| Incident runbooks | Onboard on-call staff | Yes — future-me is a stranger |
| RFC approval board | Align stakeholders | No — a shared doc is enough |
The column on the right is the whole playbook. Keep leverage, cut theater. Let me get specific about both sides.
If I could only import three things from my time in bigger orgs into a two-person shop, these are the three. They share a property: they make the machine work for you, not for the crowd.
Yes, even at two people. Not because I don't trust my co-founder, but because a second set of eyes catches the thing you were too close to see. We don't do heavyweight review with approval gates and required sign-offs on every line. We do this: nothing hits main without a pull request, and the other person skims it when they surface. For anything touching money, auth, or the data model, the skim becomes a real read.
The rule that makes it work: self-review first. Before I request review, I read my own diff top to bottom as if I'd never seen it. Half my review comments, I catch myself. That habit costs five minutes and is the single highest-leverage thing on this list. It works because the reviewer who knows your intent best is you, ten minutes later, pretending you don't.
A concrete edge case worth naming: async review at n=2 only works if a PR isn't blocking. If my co-founder is asleep in another timezone and I've self-reviewed a low-risk change, it ships — the review happens after the fact, and we revert if it's wrong. Reversibility is what buys you the right to skip the synchronous gate. High-risk changes (auth, billing, schema) wait for the real read. Match the review weight to the blast radius, not to a blanket policy.
A continuous-integration pipeline is a tireless second engineer who never gets bored of running the tests. On our Flutter apps, every push to a branch runs analyze, format-check, and the full test suite before I'm even done reading Hacker News.
# .github/workflows/ci.yml — the whole thing, no clevernessname: cion: [push, pull_request]jobs: verify: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: channel: stable - run: flutter pub get - run: dart format --set-exit-if-changed . - run: flutter analyze - run: flutter test
That's under 20 lines and it has caught more of my mistakes than any human reviewer ever could, because it never gets tired and it runs on everything. GitHub Actions gives a small team plenty of free minutes — keeping the bill at zero is a hard rule for us, and CI fits inside it comfortably.
Two upgrades I'd add the moment the basics are green. First, cache your dependencies so the pipeline stays fast — a slow CI run is one you'll start skipping, and skipped CI is no CI. Second, once you have a few integration tests, gate the merge on them, not just run them. The whole value of CI at two people is that it's the one reviewer who's never on a plane. The moment green stops meaning "safe to ship," you've lost the leverage and kept the cost.
Not RFCs. Not a review board. Just a one-page doc — call it an architecture decision record if you want the industry term — before I build anything I can't easily undo: a schema, an API contract, a pricing model, an auth flow. The value isn't approval. It's that writing forces you to notice the hole in your own plan before it's shipped and load-bearing. If I'd written this doc for that Firestore collection, the flight-night outage never happens.
My doc template is four headers and nothing else:
## Problem — what actually hurts, in one paragraph## Options — 2-3 approaches, with the tradeoff of each## Decision — what we're doing and the one reason why## Reversibility — how expensive is it to undo this later?
That last header is the one people skip and the one that matters most at small scale. Reversible decisions get made fast and loud. Irreversible ones — a Firestore data model, a public API shape, a database you'll build a year of features on top of — get the doc. The discipline isn't "document everything." It's "spend a page of writing exactly where a wrong call is expensive to walk back."
Everything below is real, useful process at scale. At two people it's cargo cult. I've thrown out all of it and don't miss any of it.
The pattern: throw out anything whose only purpose is coordinating people who don't need coordinating. Keep it in your back pocket for the day you actually have twelve people — none of these are wrong, they're just wildly premature.
The cheapest engineer you'll ever hire is a script. Before I think about a third human, I ask what I'm doing repeatedly that a machine should own. At two people, every hour of yours is roughly half the company's engineering capacity — so the ROI on automation is absurd.
My rule of thumb: if I've done a manual task three times, the fourth time I automate it. Deploys, release notes, backups, dependency bumps, changelog generation. None of it is clever. All of it removes me from a loop I don't need to be in.
#!/usr/bin/env bash# release.sh — one command, cut a release, zero ceremonyset -euo pipefailversion="$1" # e.g. ./release.sh 1.4.0git tag -a "v$version" -m "release $version"flutter build apk --release --split-per-abigh release create "v$version" \ build/app/outputs/flutter-apk/*.apk \ --generate-notes # GitHub writes the notes from commitsgit push --tagsecho "shipped v$version"
We ship binaries to GitHub Releases instead of paying for distribution infra — same zero-cost discipline as the CI. The point isn't this exact script. It's that "the deploy" stopped living in my head and started living in a file, where it can't be forgotten, half-done, or done wrong at midnight. A deploy that only one person can run correctly is a bus factor of one wearing a hoodie.
One caution I've learned the expensive way: automate the steps, not the judgment. The script cuts the release; a human still decides whether to cut it. Don't wire up an automation that pushes to production on a schedule with no one watching — that's not leverage, that's a loaded gun with a timer. Automate the boring, keep the decision.
There is no rotation when you're two. There is no follow-the-sun. There is you, and there is your phone, and there is the hope that the thing doesn't break while you're at dinner. So the strategy shifts from responding faster to needing to respond less.
Engineer for boring. The best incident is the one that never pages you. That means:
And when it does break, the runbook is what saves your sleep. Not a wiki — a single INCIDENTS.md in the repo, right next to the code it's about:
## Symptom: users report login failures1. Check Firebase Auth status page (link)2. Check `auth` error rate in the console dashboard (link)3. If spiking after a deploy → roll back: `./rollback.sh`4. If spiking with no deploy → likely quota; check billing dashboard
Future-me, woken up and stupid with sleep, doesn't need to be clever. He needs a checklist. Write the runbook the moment you finish firefighting, while the panic is still fresh — that's when you actually know what the steps were. Every line in ours was paid for by an incident, which is also the reason it's short: we only wrote down the failures that actually happened, not the ones we imagined.
Here's the trap: at two people, docs feel pointless because both of you already know everything. Then you hire a third person and discover your entire architecture lives in two heads and zero files, and onboarding takes a month you don't have. Or worse, one of those two heads is on a plane and the other is guessing.
I don't write a documentation portal. I write the three docs a smart new hire actually needs on day one, and I keep them in the repo where they can't drift into a forgotten wiki:
README that actually boots the project. Clone, one setup command, run. If a new hire can't get the app running in 30 minutes from the README alone, the README is the bug — and the fix is to test it against a fresh machine, not to add more prose.ARCHITECTURE.md — the map, not the territory. One diagram and a page of prose: what the major pieces are, how data flows, and why the big decisions were made. The "why" is the part that never survives in code and is the most expensive to reconstruct — as I learned at 30,000 feet.DECISIONS.md — a thin log of the design docs. Two or three sentences per irreversible call: what we chose, and the one reason. Nobody reads it until the day they're about to reverse a decision and need to know why it was made. On that day it's worth its weight in gold.The test for all three: could someone smart join, read only these, and start shipping without a two-hour download from my brain? If yes, you've bought back your own time and de-risked your bus factor in one move. Keep them in the repo, not in Notion — docs that live next to the code get updated in the same pull request; docs that live elsewhere rot the day you ship the change they describe.
The failure mode on the other side is turning back into the thing you escaped. You hire three people, panic about "process," and suddenly there's a standup, a Jira board, and an approval chain — the exact ceremony you correctly cut at two.
The way through is to add process reactively, tied to a real pain, not preemptively. Every rule earns its place by solving a specific hurt:
Ask one question before adding anything: what specific pain does this remove, today? If the honest answer is "it's what real companies do," don't add it. The best-run small teams I know keep the discipline — review, CI, written decisions, runbooks — and keep resisting the ceremony — standups for standups' sake, estimation theater, meetings that should be docs — for as long as they possibly can. Process should trail your headcount by a step, never lead it.