July 1, 2026
Architecture Decisions That Age Well — What We Look for Before Writing a Line of Code
The most expensive software decisions aren't made during development — they're made in the first architecture conversations. Here are the questions we ask before any project starts.
The cost of architecture decisions
Most software projects fail not because developers wrote bad code, but because early architectural decisions made good code impossible to write later. The choice of data model, the approach to multi-tenancy, the decision about where business logic lives — these seem abstract at the start and become very concrete constraints within 18 months.
We've inherited enough projects to have strong opinions about what ages well and what doesn't. This is what we look for before we write anything.
Separation of concerns
The first thing we ask: where does business logic live?
In projects that age poorly, business rules are scattered — some in the database as triggers, some in the frontend components, some in API endpoints, some in middleware. When the rules need to change (and they always do), no one can be confident they've found every place the logic is expressed.
In projects that age well, business logic lives in one place: a domain layer that's independent of the delivery mechanism (HTTP, cron job, background worker) and independent of the storage layer. This sounds like overhead at the start. It becomes essential by month 12.
Multi-tenancy from day one
For any system serving multiple clients, organizations, or branches, the data isolation model needs to be decided before the first table is created. The two main approaches — shared schema with a tenant_id column versus separate schemas per tenant — have different trade-offs that become very difficult to change after the fact.
We default to shared schema with row-level security for most projects. It's easier to operate and scales reasonably well to hundreds of tenants. Separate schemas make sense when tenants need strong isolation guarantees or custom schemas — but they make migrations and reporting significantly more complex.
The wrong time to make this decision is after you have 50 tenants and 2 years of data.
Event sourcing vs. state storage
Most systems store current state: the balance is 5,000, the status is "approved", the quantity is 24. This is simple to implement and easy to query. It's also lossy — you lose the history of how that state was reached.
Event sourcing stores every change as an immutable event: "purchase_approved at 14:32 by user_42", "stock_received 24 units batch B221". Current state is derived from the event stream. This is more complex to implement but makes audit trails, debugging, and reversibility essentially free.
We use event sourcing for systems where audit history matters legally or operationally — financial transactions, medical records, inventory movements, approvals. For simpler domains, plain state storage is usually the right call. The key is making the choice deliberately, not defaulting to state storage because it's familiar.
API design for longevity
Internal APIs that were designed "just for this frontend" tend to accumulate implicit contracts. The frontend starts depending on field ordering, on undocumented fields, on error response shapes that weren't part of a spec. When a second consumer appears — a mobile app, a webhook, a partner integration — the API has to serve multiple masters and was designed for none of them.
We design APIs as if they'll have three consumers we haven't met yet. This means explicit versioning from the first endpoint, a consistent error response format, and no fields that exist only because the frontend happened to need them this week.
The question we always ask
Before committing to an architecture decision, we ask: "What would we have to change if this assumption turned out to be wrong?"
If the answer is "everything", we look for a way to make the decision reversible. If the answer is "a well-defined module", the decision is probably fine. The goal isn't to predict the future — it's to avoid building systems where wrong assumptions are fatal rather than recoverable.
This is what we mean by building for longevity. Not overengineering, not building for scale you don't have yet, but making the decisions that will still look reasonable two years from now when the business has changed in ways no one predicted.