Every significant web application you build will be defined β and potentially constrained β by the architecture decisions you make in the first few weeks of development. The choices between monolith and microservices, REST and GraphQL, server-rendered and client-rendered, SQL and NoSQL: these aren't just technical preferences. They're bets on how your application will grow, how your team will scale, and what problems you'll be debugging at 2 AM two years from now.
This is a frank guide to the decisions that matter most, with the perspective of having made (and sometimes regretted) most of them in production.
1. Monolith vs Microservices: The Real Trade-Off
The software industry spent a decade evangelising microservices and is now spending another decade quietly re-evaluating the decision. The honest assessment:
Monoliths win when: your team is under 20 engineers, you're still validating product-market fit, your domain boundaries are unclear, or the operational complexity of distributed systems is not justified by the scale or team size. A well-structured monolith β with clean module boundaries, dependency injection, and clear separation of concerns β is genuinely easier to develop, test, deploy, and debug than the equivalent functionality split across 12 microservices.
Microservices win when: different parts of your system need to scale independently (the checkout service needs 10Γ the compute of the browse service), different teams own different domains with different release cycles, or you have genuine polyglot requirements (ML service in Python, API in Go, CMS in PHP).
The modular monolith is the most under-discussed architecture: a single deployable unit with strongly bounded internal modules. Start here. Extracting a service from a modular monolith is straightforward β the boundaries are already clean. You cannot easily go in the other direction.
2. API Design: REST, GraphQL, and tRPC
The API paradigm wars of the 2010s have settled into a more pragmatic landscape:
- REST remains the right default for public APIs, third-party integrations, and resource-based CRUD. HTTP semantics, cacheability, and tooling are mature and universally understood.
- GraphQL earns its complexity when: you have multiple clients (mobile, web, kiosk) with different data needs, your data is a graph with complex relationships, or over-fetching and under-fetching are genuine performance problems. Don't choose GraphQL to be modern β choose it because it solves a real problem you have.
- tRPC is the right choice for TypeScript full-stack monorepos (Next.js + Node backend). It provides end-to-end type safety with zero schema duplication, at the cost of being TypeScript-only.
- gRPC for internal service communication where performance and strong typing matter more than HTTP conventions.
3. Database Architecture Choices
Relational databases (PostgreSQL, MySQL) remain the right default for the vast majority of web applications. Choose a different persistence model only when you have a specific, documented reason:
- PostgreSQL over MySQL for new projects: better JSON support, window functions, full-text search, and a more rigorous standards compliance track record
- Redis for caching, sessions, rate limiting, and pub/sub β not as a primary datastore
- Elasticsearch / OpenSearch for full-text search, log analysis, and analytics β not for transactional data
- Vector databases (pgvector, Pinecone, Weaviate) for AI/ML workloads requiring semantic similarity search
- Document databases (MongoDB) only when your data genuinely has no fixed schema and relationships are rare
The most common database architecture mistake is using the wrong tool because it was fashionable β MongoDB for data that obviously wants to be relational, Redis as a primary database, ElasticSearch for data that just needs an index.
4. Frontend Architecture: SSR, SPA, and Islands
The frontend rendering pendulum has swung from server-rendered (2000s) β SPAs (2013β2020) β server-first (2021βpresent). The current state:
- Next.js / Nuxt.js App Router with React Server Components is the current best practice for content-heavy applications. Pages are rendered on the server, sent as HTML, then hydrated selectively β best-of-both performance characteristics.
- Islands Architecture (Astro, Fresh) sends zero JavaScript by default and hydrates only interactive components. Ideal for content sites with minimal interactivity.
- SPAs (React, Vue without SSR) are still appropriate for authenticated dashboards and tools where SEO and initial load time are less critical.
5. Security by Default
Security is not a feature you add β it's a default you maintain. The non-negotiable baseline for every web application:
- HTTPS everywhere, HSTS preloading, proper TLS configuration
- Content Security Policy headers
- Parameterised queries β no string interpolation in SQL
- CSRF protection on all state-changing requests
- Dependency vulnerability scanning in CI (Dependabot, Snyk)
- Secrets management via environment variables or Vault β never in code
- Rate limiting on authentication endpoints
6. Performance Engineering
Performance is architecture, not optimisation. Decisions made early determine your performance ceiling:
- Cache at the right layer: CDN edge β full-page cache β fragment cache β database query cache
- Database query performance: measure with EXPLAIN ANALYZE before adding an index
- Async/queue expensive operations: email sending, image processing, report generation
- Asset optimisation: modern image formats (WebP, AVIF), code splitting, tree shaking
- Measure with real user metrics (Core Web Vitals), not just synthetic tests
7. The Technology Selection Framework
When evaluating any technology for a production project, we ask five questions:
- Does it solve the actual problem? (Not a hypothetical future problem)
- Can our team support it? (Hiring, learning curve, community)
- What does failure look like? (Assess operational risk explicitly)
- What's the total cost of ownership over 5 years? (Licensing, hosting, maintenance)
- What's the exit strategy? (Vendor lock-in, data portability)
Technology decisions are investments, not experiments. The architecture you choose this year will be a constraint β or an asset β for the next five. Choose deliberately, document the reasoning, and revisit the decisions as the context changes.