Architecture decisions that actually matter
Most architecture diagrams are decoration. Five decisions actually move the needle on cost, speed, and survivability. The rest is taste.
The five decisions that matter
Most of what gets called architecture is actually preference. Five decisions genuinely shape the cost, speed, and lifespan of a system. Get these right and most other choices become recoverable. Get them wrong and no framework choice will save you.
- The deploy unit. One service or many. This is a one-way door for years.
- The data model. The shape of your tables determines what queries are cheap and what features are even possible.
- The transaction boundary. What is allowed to be inconsistent, and for how long, and where the user sees it.
- The integration boundary. Where you trust other systems and where you don't, and what you do when they're down.
- The deletion plan. How you remove a piece of the system. If you can't answer this, you're collecting liabilities.
The database choice (and the boring answer)
For 95% of new systems, the right database is Postgres. It's not a fashion statement. It is, in 2026, the most under-used database among teams that still pick something else by default.
Postgres gives you transactions, joins, JSONB for the parts you actually want schemaless, partial indexes, materialized views, full-text search, listen/notify, row-level security, and a queue table that will outlast three of your microservices. The operational story is well-understood by every senior backend engineer in Europe.
Pick something else only when the workload genuinely doesn't fit: very high write throughput on time-series data (Timescale, Clickhouse), genuine document-store needs with no relational tail (MongoDB), real-time collaborative state (Convex), or vector search at scale (a dedicated index, not your primary store).
One deploy unit, until proven otherwise
The single most expensive architectural decision teams make is splitting a system into services before they have to. The microservices conversation belongs in its own essay, but the heuristic is simple: if you don't have multiple teams who own slices end-to-end, including on-call, you don't have the org for microservices.
A modular monolith with explicit module boundaries gives you 80% of the benefits people want from microservices, at 10% of the operational cost. When the day comes that you genuinely need to extract a service, you extract a module. The boundary is already there.
Boring is not the same as unsophisticated. Boring is what survives the engineer who picked it.
Transactions and consistency
Eventual consistency is not a free architectural feature. It is a UX problem you have moved off the database and onto the user. Every "the cart is empty for three seconds after I added something" is an architectural decision the user is paying for.
Default to strong consistency inside a single deploy unit. Reach for eventual consistency only when latency, scale, or geography genuinely demand it, and when product has agreed in writing that the inconsistency is acceptable in the user-facing flow.
Background work and queues
A queue table in your existing database is a serious primitive. SELECT FOR UPDATE SKIP LOCKED on Postgres handles tens of thousands of jobs per second on hardware that costs nothing. You get transactional enqueue with the rest of your write, no extra infrastructure, no second source of truth.
Reach for a real broker (Kafka, NATS, Pub/Sub, SQS) when you need fan-out to multiple independent consumers, when one queue would be a contention point across teams, or when you need replay semantics. Not before.
Caching is debt with a hit rate
Every cache layer you add is a new failure mode: stale reads, thundering herds, invalidation bugs, the cold-cache event when the cache fleet restarts. Add caching only when you have a measured latency or load problem, not because you read a blog post.
When you do add a cache, write the invalidation plan first. If you can't articulate when an entry is removed, your cache is a feature flag for serving wrong data.
Integration boundaries (and the on-call story)
Every external system you depend on becomes part of your uptime story. Stripe, your CRM, your CMS, your auth provider, your analytics — when they're down, you're down, unless you wrote the fallback.
- Treat third-party calls as if they will time out, retry, double-fire, and rate-limit. Because they will.
- Wrap them in a thin adapter so you can swap them or stub them in tests.
- Decide explicitly what your app does when each one is unavailable. Write it down.
- Cache the responses you can. Idempotency keys on the writes you can't.
Want this kind of judgment on your project?
I read every email within one working day. Bring a project, a quote, or a system you're stuck on.
15 things every Contentful enterprise project gets wrong in the first 6 weeks
The 15 production gaps every enterprise Contentful + Next.js build hits in the first six weeks — and how to close each one without burning a sprint. A pre-kickoff checklist for technical leads on a Contentful enterprise starter.
REST + GraphQL hybrids for multi-locale CMS-driven sites
Why neither REST-only nor GraphQL-only is the right call for an enterprise multi-locale CMS site, and how to split by concern instead. Includes the circular-reference problem on full REST payloads, the bundle cost of GraphQL on the client, the decision matrix per call site, the unified fetcher, granular cache tags, the block-as-fragment pattern, locale fallback in one round trip, Live Preview survival, Server Actions for CMA writes, the Algolia Sync API exception, and the migration sequence.
The consultancy playbook
How digital consultancies operate, how they price, and where Danish clients typically overpay.