How I Evaluate Architecture Before Writing a Line of Code
I've shipped enough half-baked systems to have a strong opinion on this: refactoring is not a strategy, it's a tax you pay for skipping thinking. I'd rather spend two extra days on the whiteboard than two extra sprints untangling a state management decision that should've taken an afternoon to get right.
This isn't about big upfront design in the waterfall sense. It's about front-loading the expensive-to-reverse decisions and being deliberately lazy about everything else. Here's the actual process I run through, mentally or on paper, before touching a code editor.
1. I separate decisions into "cheap to reverse" and "expensive to reverse"
Most engineering advice about "just start coding" is fine — for the 80% of decisions that are cheap to undo. Component naming, folder structure inside a feature, which utility library you reach for. None of that matters enough to plan around.
What I actually spend time on upfront: data model and ownership boundaries, state management topology, tenant/permission boundaries on multi-tenant systems, and integration seams where my system talks to something I don't control.
If I get the data model wrong on a multi-tenant PIM platform, I'm not fixing that with a refactor — I'm fixing it with a migration, downtime, and an uncomfortable conversation. If I get a component name wrong, I hit rename in five seconds. I calibrate my planning effort to the cost of being wrong, not to the size of the feature.
2. I write down the constraints before I write down the solution
This sounds obvious and gets skipped constantly. Before I sketch any architecture, I force myself to answer three things.
On an e-invoicing platform handling GDPR/HIPAA-adjacent data, "guarantee" has legal teeth — audit trails and data residency aren't nice-to-haves, they're the spec. Scale means the actual day-one numbers, not the scale we hope for in year three. And "who maintains this in a year" matters more than people admit — architecture for a two-person frontend team looks different from architecture for a team that's about to triple.
I've had to design activity-tracking systems that ran on both desktop (Electron) and web from a shared core — if I hadn't nailed down "what's platform-specific vs. shared" as a constraint before writing code, I'd have ended up with two codebases pretending to be one.
3. I sketch the boundaries, not the implementation
At this stage I'm not writing interfaces or types. I'm drawing boxes and arrows — literally, on paper or a whiteboard app. The question I'm answering is: where are the seams?
A seam is any place where I want to be able to change one side without touching the other. The nursing staffing platform I worked on had a hard seam between scheduling logic (which changes per client contract) and the core shift/worker data model (which shouldn't care about any single client's rules). Get that seam wrong and every new client becomes a special case bolted onto core logic — I've seen that turn into an unmaintainable if-ladder within two quarters.
4. I build the riskiest slice first, not the easiest one
This is where I differ from a lot of "ship fast" advice. I don't start with the easy, confidence-building feature. I start with whatever part of the architecture I'm least sure will actually work.
Usually that's an integration point or a performance-sensitive path — real-time sync over Socket.io, a Zustand store shape that needs to stay sane across a dozen feature slices, a Strapi content model that has to support multi-tenant overrides without duplicating schemas. If that risky slice falls over, I want to know on day two, not week six, when three other features have been built on top of the assumption that it works.
5. I ask "what does this look like at 10x?" — briefly
Not exhaustively — over-engineering for a scale you'll never hit is its own failure mode, and I've learned to catch myself doing it. But a quick gut-check matters.
If tenant count went 10x, if data volume went 10x, does the architecture bend or does it break? If it bends — slower queries, more instances, tunable knobs — fine, ship it. If it breaks — fundamental redesign required — that's a signal to reconsider now, while it's cheap.
6. I dig in alone first, then bring it to someone who'll disagree with me
I default to working through the design myself before pulling anyone else in — partly because I think better once I've hit a few walls solo, partly because I want to show up to a design conversation with an actual position, not a blank page.
But once I've got a draft architecture, I deliberately look for someone who'll poke holes in it rather than nod along. The value of review isn't validation, it's finding the failure case I was too close to see.
None of this is exotic. It's a bias toward making the expensive decisions early and deliberately, and staying lazy about everything else. The whole point of being architecture-first isn't to be slow — it's that thinking for two days up front is consistently cheaper than refactoring for two weeks later, and I'd rather pay the cost when it's small.