Boring on purpose: what we build first and why
There's a version of every project where you start with the exciting part: the AI feature, the animation, the clever architecture. We've learned to start with the opposite — the plumbing that has to be right for anything else to matter. It makes for worse demos in week two and much better software in month six.
The exciting part is usually the replaceable part
On a construction management platform, the feature people ask about is the AI. It drafts an owner update by pulling budget status, schedule health, and progress since the last update, and writes the narrative a project manager would otherwise spend twenty minutes on.
It's genuinely useful. It is also the single most swappable component in the system. It runs through an abstraction that keeps the provider a configuration detail, and if a better model appears next quarter, changing to it is an afternoon.
The payments layer is not like that. Neither is the data model that says what a project, an invoice, and a change order actually are. Those decisions propagate into every screen and every integration, and revisiting them in month six means touching everything. So they get built first, carefully, while there's still room to be wrong about them.
Build the failure path before the happy path
Most software is written for the case where everything works, then patched for the case where it doesn't. We try to invert that when the failure case is the common one.
For an app used in rooms with unreliable wifi, we built the offline path first: local storage as the source of truth, anonymous sessions that can upgrade to real accounts later, and enough bundled content that the app is fully usable with no backend configured at all. Sync came second, as an enhancement to something that already worked.
The test isn't "does it handle being offline." It's "does it degrade to useful, rather than degrading to a spinner." That's a different design, and you can't retrofit it. In practice that means asking, for each screen, what it shows when the data isn't there — not as an error state bolted on at the end, but as the first thing you design. A screen that has a real answer for empty, stale, and failed is a screen that rarely surprises anyone in production.
- Great demo in week two
- Data model decided under deadline
- Failure paths patched on at the end
- The rewrite lands in month six
- Dull screenshot in week two
- Data model settled while it's cheap
- Failure paths designed, not discovered
- Features land fast in month six
The exciting part is usually the replaceable part. Build the thing that everything else has to sit on.
Conservative code where the cost is asymmetric
Not all code deserves the same caution, and pretending otherwise is its own kind of waste.
Money paths get the most conservative code we write. Explicit state machines, no silent retries, reconciliation that assumes the network lied. We also carry a second payment provider as an alternative path — not because the first one is bad, but because a payments integration you can't swap is a business risk wearing a technical costume.
Meanwhile, a marketing page animation can be clever and a little fragile, because the worst case is that it looks wrong for an afternoon. Spending equal care on both is a way of spending too little on the part that matters. The useful question isn't how likely a failure is, it's who finds out and how long it takes them. A rendering glitch announces itself immediately to the person looking at it. A payment that silently didn't settle announces itself days later, to someone who is now angry, and by then the state you'd need to debug it has moved on.
Boring means legible to the next person
The other reason we default to boring: someone else reads this later, and quite often that someone is us, a year on, with no memory of why.
Clever code has a shelf life measured in how long its author remembers the trick. Boring code — explicit names, obvious control flow, comments that explain the constraint rather than the syntax — survives handover. Since we build a lot of software that other teams eventually own, legibility is a deliverable, not a preference.
This is also why we're careful with abstractions early. An abstraction written before you've seen the third case is usually wrong, and wrong abstractions are harder to remove than duplication is to consolidate.
When this is the wrong instinct
Building the foundation first is a good default and a bad religion. There are cases where it's actively wrong, and knowing them matters as much as the rule.
The clearest one is when you don't yet know what you're building. If the real question is whether an interaction feels right, or whether a parsing approach can work at all, then the fastest possible throwaway answers it — and treating that prototype like production is a way of spending a month to learn something a day would have taught you. The mistake there isn't building carelessly, it's failing to throw it away afterward. Prototypes that quietly become the codebase are their own genre of disaster.
The other case is a genuine deadline with a hard external edge — a trade show, a compliance date, a funding milestone. Then the right call is sometimes to build something you know you'll rewrite, name that debt out loud, and schedule the rewrite before you start. What we try never to do is take on that debt accidentally, or pretend the shortcut was the plan.
What this looks like in practice
Week two is less impressive. There's a data model, an auth flow, a deployment pipeline, and something ugly that proves the hardest integration works end to end. Nobody is excited by the screenshot.
Month six is where it pays. Features land quickly because the foundation holds them. The failure cases were designed for rather than discovered. And when something does need to change, it's usually the layer we deliberately made cheap to change.
It's not a rule we apply blindly — a prototype meant to answer one question should be built fast and thrown away, and treating it like production is its own mistake. But for software someone will run their business on, boring first has never once been the thing we regretted.