The Most Expensive Architectural Decisions Are the Ones You Don't Realize You're Making

Most costly architecture choices never show up on a design doc. They feel like getting things done.

FAQ

What are invisible architectural decisions?

Choices that never show up on a design doc because they feel like the obvious path. Importing a payment SDK in a resolver, checking if (role === 'admin'), binding product behavior to a framework's routing model. They compound into rewrite tax later, usually without anyone remembering when the decision got made.

What does 'optionality is an architectural investment' mean?

Spending a bounded amount of design and code upfront so future switches stay cheap. A thin contract around a queue, a mailer, or a payment processor is the premium. The payoff is changing providers without rewriting every call site, or at least only rewriting one place.

How do I measure whether an architecture is good?

Ask what a realistic change costs in calendar time and blast radius. Can you swap a mailer without touching GraphQL? Can you add a permission without hunting every role string? If the answer is a multi-week rewrite, something went wrong earlier, regardless of how clean the tree looks.

Is putting every vendor behind an interface over-engineering?

Not for anything that touches money, identity, messaging, or the primary data plane. Skip it for throwaway scripts. For SaaS core paths the interface is cheap insurance compared to the rewrite after lock-in.

Why do teams leave Next.js or BaaS stacks later?

Often because product needs outgrew defaults they never treated as commitments. Hosting assumptions, auth models, and data access patterns leak into domain code. Leaving then means rewriting product rules that should have lived behind a thin contract. The tool itself was usually fine on day one.

Should billing just be Stripe?

Stripe (or any processor) is fine as the speed bet. What fails is treating vendor price ids as your product model. Persist internal product and price rows, resolve external ids at the processor boundary, and keep the payment contract's inputs and outputs in your domain types. Webhook ingress can stay vendor-specific. GraphQL and utilities should not import Stripe.Price.

Why does architecture need to be legible to AI coding tools?

Architecture that only exists in one person's head fails when that person is out, and again when an LLM proposes an edit that crosses a boundary it can't see. Short hard rules (payment SDK only in the processor implementation, one finalizer for checkout) shrink the context window for humans and models. That isn't anti-AI. AI is infrastructure, and infrastructure needs fences.