Contents
Over twenty years, web applications have gone from simple PHP pages with full reloads to distributed systems with dozens of microservices, containers in Kubernetes, and embedded language models. Each time, the industry announced a "new era" - and each time, a few years later, the previous approach became relevant again, just in a different form. Server-Side Rendering came back in Next.js and Astro; monoliths returned to serious discussion under the name modular monolith; REST is now complemented by GraphQL and gRPC.
Web application architecture changes not because engineers love fashionable buzzwords. It changes because the constraints change: internet speed, user device power, team size, availability requirements, and the cost of cloud infrastructure. What was the right solution for 50,000 users and one server becomes a bottleneck at five million users and forty developers - and sometimes the opposite is also true.
The PHP monolith of 2005 was not a mistake. Netflix-style microservices are not automatically a fit for a five-person startup. SPA did not "kill" server-side rendering - it solved a different problem and created problems of its own. The main idea of this article: every architecture was a response to the constraints of its time. Once you understand that chain, it becomes easier to choose tools today - and avoid repeating someone else's mistakes under the banner of "doing it like the big companies."
Key takeaways
Architecture is a trade-off, not a medal. There is no single "best" stack for every case. There is a set of trade-offs: development speed vs scalability, deployment simplicity vs team independence, rich UX vs SEO and initial load performance.
Technology is cyclical. Server-side rendering -> AJAX -> SPA -> SSR again. Monolith -> microservices -> modular monolith. Every turn of the spiral keeps the lessons of the previous generation.
Copying Big Tech is dangerous without their context. Netflix split its monolith when it had hundreds of engineers and its own cloud platform. A startup with three developers will get a distributed monolith - all the complexity, none of the scaling benefits.
Observability and data have been part of architecture since the 2010s. Logs in stdout are not enough for systems with twenty services. Metrics, traces, and polyglot persistence are not "infrastructure luxury" - they are part of what makes a system maintainable.
AI adds a new layer in the 2020s. Vector databases, RAG, and LLM call orchestration are not "just another API" but a separate architectural axis alongside the classic backend.
Introduction: why architecture never stands still
A web application is always an agreement between three forces: what the user wants (fast, convenient, reliable), what hardware and the network can do (latency, bandwidth, phone battery), and what the team can handle (headcount, experience, infrastructure budget). When any of those forces shifts, architecture gets reconsidered.
Audience growth pushes teams toward horizontal scaling and caching. Faster internet and the arrival of 4G/5G made it practical to move logic into the browser - hence SPAs and heavier frontends. More powerful smartphones made JavaScript viable in places where it used to lag on every click. Cloud platforms lowered the price of experimenting with distributed systems - while also increasing the price of operational mistakes.
It is important to separate obsolete from wrong. CGI scripts in Perl were a reasonable choice in 2003: simple deployment, a clear model, minimal dependencies. The fact that today you would choose FastAPI or Rails for a new project does not make Perl "bad" - it solved the problems of its era within the constraints that existed at the time.
Chapter 1. The early-2000s web: the era of server-side rendering
What a typical site looked like in 2005
The typical flow was linear: the browser sends an HTTP request -> the web server (Apache, IIS, nginx in its early days) forwards it to the application -> the application reads data from MySQL or PostgreSQL -> the template engine assembles HTML -> the server returns a finished page. JavaScript on the page, if present at all, was mostly limited to form validation, simple effects, and counters. Any action - changing a filter, adding to cart, posting a comment - usually meant a full document reload.
For users, that meant visible pauses and the "flash" of a white screen. For developers, it meant predictability: all logic on the server, state in the session or cookies, debugging through logs on a single machine.
Popular technologies
| Technology | Role |
|---|---|
| PHP | Dominated shared hosting; WordPress, phpBB, early social networks |
| Perl + CGI | Corporate forms, early online stores |
| ASP.NET | Enterprise apps on the Windows stack |
| Java Servlet / JSP | Large portals, banking, public sector |
| CGI | Universal but slow bridge between the web server and any language |
The shared pattern was simple: the server-side language generates HTML. The client is a thin terminal for rendering it.
Typical architecture in 2005
[Browser] -> [Web Server] -> [Monolithic Application] -> [Template Engine] -> HTML
↓
[Database]
Three layers, one process (or a cluster of identical processes behind a load balancer). Deployment meant uploading files via FTP or restarting a single WAR file. No separate frontend builds, no API contracts between teams, no container orchestrators.
Pros and cons
Pros: low barrier to entry; one repository and one deployment; SEO out of the box because search engines see ready-made HTML; relatively simple debugging and monitoring.
Cons: every click is a server round-trip; rich interactive UI is hard to build; the server spends CPU rendering pages on every request; horizontal scaling runs into session handling and the shared database.
This model never disappeared. It evolved - into MVC frameworks, into a new generation of SSR frameworks, into the HTMX-style idea of "HTML from the server, minimal JS." But in 2005, it was the only mass-market answer to the question, "How do you build a site with data?"
Chapter 2. The arrival of AJAX and the birth of Web 2.0
What changed
In the mid-2000s, pages stopped reloading completely on every action. The browser started requesting only the data - JSON, XML, or a chunk of HTML - and updating part of the DOM. Users got the feeling of "an application inside the browser" without installing a desktop client.
The AJAX revolution
AJAX (Asynchronous JavaScript and XML) was built on XMLHttpRequest, an API that first appeared in Internet Explorer 5 but became mainstream after standardization and examples from Google. An asynchronous request did not block the UI; the response was handled by a callback and updated the interface locally.
The key shift was this: the server returns data, the client decides how to present it. The boundary of responsibility moved.
Projects that changed the industry
- Gmail (2004) - email without reloading the tab; it proved the web could compete with desktop software.
- Google Maps (2005) - panning and zooming without a round-trip for every action; it showed the value of fluid UI.
- Facebook - feeds, likes, and real-time notifications; it scaled the pattern to hundreds of millions of users.
These products did not "invent JavaScript" - they demonstrated the business value of a rich client. Competitors had to copy the UX.
New problems and consequences
Logic was now split between server and browser without clear contracts. Validation got duplicated: on the client "for convenience," on the server "for security." Page JavaScript grew from kilobytes to megabytes. The first spaghetti code made of callback hell started to appear.
Architectural consequence: this began a long transfer of state and behavior into the client. Ten years later that would lead to SPA; in 2007 it meant the first REST-like endpoints built "for the frontend."
Chapter 3. The MVC framework era
Why MVC frameworks appeared
As AJAX applications and page counts grew, monolithic scripts with "everything in one file" stopped scaling organizationally. Teams needed conventions: where business logic lives, where presentation lives, where requests are handled. MVC (Model-View-Controller) gave everyone a shared vocabulary.
Popular solutions
- Ruby on Rails (2004) - "convention over configuration," Active Record, generators; it accelerated startups.
- Symfony, CodeIgniter, Yii, Laravel - the PHP ecosystem with mature ORMs and package ecosystems.
- ASP.NET MVC - Microsoft's answer to Rails for the .NET world.
- Django, Spring MVC - Python and Java frameworks built around the same separation.
The MVC concept
- Model - data and business rules (often through an ORM).
- View - the template the user will see.
- Controller - receives the HTTP request, calls the model, chooses the view.
Requests move through a predictable path; a new developer can understand the project structure in a day instead of a month.
What MVC architecture gave teams
Cleaner code, reusable partial templates and layouts, faster development of CRUD applications. It also introduced deployment conventions: database migrations, environments, and asset pipelines.
MVC limitations
Fat controllers - all the logic settled into a layer that was supposed to do routing only. Fat models - Active Record mixes persistence and domain logic. Monoliths kept growing: one repository, dozens of modules, one shared database. MVC organized code inside the monolith, but it did not solve the problem of scaling teams - that led to services and later microservices.
Chapter 4. JavaScript becomes a full frontend language
Browser capabilities grow up
The V8 engine (Chrome, 2008) and the browser race around JIT compilation speed made JavaScript suitable for thousands of lines of logic, not just 50-line snippets. ES5, modules, Canvas, and Web Workers arrived. JavaScript stopped being "the language for buttons."
Early frontend frameworks
| Framework | Idea |
|---|---|
| jQuery (2006) | Unified DOM manipulation and AJAX; the de facto standard until around 2015 |
| Backbone.js | Models, Views, Events; structure for early SPA-like apps |
| Knockout.js | Two-way data binding with the DOM |
| Ember.js | Batteries included for ambitious client-side applications |
jQuery did not replace server-side rendering - it brought pages to life on top of it. Backbone and Ember already assumed that the client owns the state.
The problem: the interface becomes more complex than the server
On complex screens - dashboards, editors, multi-step forms - presentation code started to exceed server code in both size and complexity. Teams began hiring frontend developers as a distinct role. Build tooling (Browserify, later Webpack) became mandatory.
The rise of SPA
Single Page Application means one HTML shell, with navigation and data handled afterward through JavaScript and APIs. The URL changes via the History API; content loads without a full page refresh. UX moves closer to native applications; the price is a heavy initial bundle and more difficult SEO unless you add extra techniques.
Chapter 5. The Angular, React, and Vue revolution
Why SPA took over the market
After smartphones and the App Store, users expected the interface to respond instantly. SPAs delivered smooth transitions, optimistic updates, and later offline caching through Service Workers. For B2B dashboards and SaaS, that became the quality baseline.
The three pillars
Angular (2010, relaunched in 2016) - a full framework: routing, DI, forms, HTTP client. TypeScript by default. The "everything included" approach works well for enterprise teams with long planning cycles.
React (2013) - a UI library with a component-based model and a Virtual DOM. It does not dictate routing or state management - the ecosystem (Redux, React Router) is chosen separately. Reusable components changed how design systems are built.
Vue (2014) - low barrier to entry, reactivity out of the box, gradual adoption in legacy pages. It grew especially fast in China and Europe.
How architecture changed
The backend became an API. The server returned HTML less often and JSON over REST more often. The frontend became a separate application with its own repository, CI, and deployment to a CDN or static host. The contract between teams was either an OpenAPI document later on - or, all too often, a painful verbal agreement.
The typical 2015-2020 stack appeared: React + Node.js (as a BFF or full API) + PostgreSQL + Redis + Docker. That separation made specialist hiring easier; it also made API versioning and release coordination more complex.
Chapter 6. The birth of API-first architecture
What API-first means
Contract first, implementation second. Teams describe endpoints, request and response schemas, and error codes before writing code. Clients (web, iOS, Android, partners) can work in parallel with the backend by using mock servers.
REST as the standard
REST (Representational State Transfer) established familiar patterns:
- Resources -
/users/42,/orders/99/items - HTTP methods - GET reads, POST creates, PUT/PATCH updates, DELETE removes
- JSON - a lightweight format for JavaScript clients
Because it is easy to explain and easy to debug with curl, REST became the dominant public API style.
Pros
Team and client independence; one backend for web and mobile apps; easier partner integrations and webhooks.
Cons
Versioning - /v1/ vs headers vs breaking changes at the same URL. Excess requests - a profile page makes five GET calls for related entities (the problem GraphQL would later try to solve). Overfetching - the client receives fields it never displays.
API-first remains a sound principle; the real argument is about the shape of the API (REST, GraphQL, gRPC, or tRPC for full-stack TypeScript).
Chapter 7. The emergence of microservices
Why teams moved away from monoliths
When fifty teams work inside one monolith, every deployment becomes a lottery: someone else's module breaks yours. The shared database becomes a bottleneck and a battlefield for migration conflicts. Horizontally scaling "the whole application" is more expensive than scaling only the reporting service or the search service.
What a microservice is
A small standalone service with its own database (ideally), its own deployment, and a clearly defined responsibility. It communicates over the network - HTTP, gRPC, or message queues.
Who popularized the model
Public stories from Netflix, Amazon ("two-pizza teams"), and Google (with internal infrastructure years ahead of the market) created a narrative: the success of giants is tied to microservices. Much less public attention was given to the cost - hundreds of platform engineers, a blameless postmortem culture, and mature monitoring.
Typical microservice architecture
[Clients] -> [API Gateway] -> [Service A] [Service B] [Service C]
↓ ↓ ↓
[Auth] [DB A] [Queue] -> [Service D]
The gateway becomes the single entry point for TLS, rate limiting, and routing. Queues (RabbitMQ, Kafka) handle asynchronous work and loosen coupling.
Advantages and drawbacks
Pros: independent deployment; scaling individual services; technology diversity (Python for ML, Go for workers).
Cons: distributed transactions and consistency issues; network failures and cascading outages; difficult local development ("start 15 containers"); the need for observability at a level a monolith never required.
Microservices are a tool for organizational scale, not a default technical upgrade. For more on the traps of growing complexity, see why ERP systems become monoliths.
Chapter 8. Containerization and Docker
Why Docker changed the market
Before Docker, a developer would say, "It works on my machine," and production would still fail because of a different libc version, a missing PHP extension, or mismatched file paths. Virtual machines solved isolation, but they were heavy: minutes to boot and gigabytes on disk.
What Docker introduced
A container is an isolated process with its own filesystem built from an image. The image is assembled from a Dockerfile and versioned in a registry (Docker Hub, ECR, GCR). The same artifact can travel from laptop to production unchanged.
Impact on architecture
Deployment sped up: docker pull plus restart instead of manual server setup. Microservices became practical - not "15 JVMs on 15 VMs," but "15 lightweight containers across a few hosts." That also created a need for an orchestration layer - without it, managing dozens of containers by hand is not viable.
Chapter 9. Kubernetes and the cloud revolution
Why Docker is not enough
At hundreds of services, you need automatic restart of failed containers, traffic load balancing, rolling updates without downtime, secrets, CPU/RAM limits, and service discovery so payments-v2 can find users without hardcoded IP addresses.
What Kubernetes brought
Declarative manifests describe the desired state (3 replicas, image app:1.2.3), and the cluster pulls reality toward that state. HPA provides autoscaling by CPU or custom metrics. Self-healing recreates pods on failed nodes.
The new reality
Infrastructure as Code - Terraform, Pulumi. GitOps - Argo CD, Flux. The term Cloud Native - applications designed for a dynamic environment rather than for an "immortal server."
The cost of success
There is a sharp increase in operational complexity. A team of eight developers rarely needs a full K8s cluster; managed platforms such as ECS, Cloud Run, Fly.io, and Railway deliberately sell "less Kubernetes in your face."
Chapter 10. The serverless approach
The idea
Do not manage servers at all - upload a function, and the platform runs it in response to an event (HTTP, queue, cron). Scaling from zero to thousands of instances becomes the cloud provider's problem.
Platforms
AWS Lambda, Azure Functions, Google Cloud Functions; higher-level platforms such as Vercel Functions and Cloudflare Workers, which are even closer to the edge.
When it is useful
Unstable traffic patterns (one request per hour vs Black Friday spikes); event-driven pipelines (file uploaded -> processing -> notification); quick API prototypes.
Serverless limitations
Cold start - the pause on the first invocation after idle time; critical for latency-sensitive APIs. Limits on execution time and package size. Vendor lock-in - tight coupling to one cloud's APIs and ecosystem. Debugging and local emulation are harder than with containers.
Serverless is not a replacement for the entire backend, but a specialized layer for short stateless tasks.
Chapter 11. The return of server-side rendering
Why SPA came under criticism
The typical first visit to a React SPA looks like this: empty HTML -> download JS (hundreds of KB to MB) -> execute it -> request data -> render. Slow first load hurts LCP and conversion on mobile networks. SEO is problematic for content sites without SSR or prerendering. Accessibility and no-JS behavior suffer when all content is client-side.
A new generation of frameworks
- Next.js - SSR, SSG, and ISR for React; the de facto standard for full-stack React.
- Nuxt - the equivalent for Vue.
- Remix - web standards, nested routes, progressive enhancement.
- SvelteKit, Astro - less client-side JavaScript, island architecture.
Approaches
| Approach | Essence |
|---|---|
| SSR | HTML on every request (or with caching) |
| SSG | HTML generated at build time |
| ISR | Static pages with background revalidation on a schedule or event |
Hybrid architecture
Static pages use SSG; the personal dashboard uses client-side interactivity; the product catalog uses SSR with CDN caching. The best of both worlds - if the team is ready for build complexity and two runtime environments (server + browser).
Chapter 12. Modern architectural styles
Modular Monolith
A monolith with hard module boundaries inside one deployment. Modules communicate through explicit APIs and do not reach into each other's tables. Easier to maintain than a distributed system; cheaper than microservices for a team of up to roughly 20 people. A module can later be extracted into a service if those boundaries have already been proven.
It became popular again after the wave of "we split the monolith and regretted it."
Microservices - when they are justified
Very large products, dozens of autonomous teams, different subsystem load profiles, and a mature platform layer (K8s, observability, SRE). Without that, you get a distributed monolith with network latency added on top.
Event-Driven Architecture (EDA)
Services publish events ("Order Created"), and subscribers react asynchronously. That gives loose coupling, scalable consumers, and auditability through an event log.
Tools include Apache Kafka (high throughput, retention), RabbitMQ (classic queues), and NATS (lightweight, cloud-native).
CQRS
Command Query Responsibility Segregation means separate models for writes and reads. Writes go into a normalized OLTP database; reads come from denormalized projections or a separate store. Teams use it under extreme read load or for complex reporting; the price is synchronization complexity and eventual consistency lag.
Chapter 13. Frontend architecture today
Component-Based Architecture
React, Vue, and Angular still build UI from reusable components with isolated state and props. The differences are mostly in the ecosystem, reactivity model, and enterprise constraints.
Design Systems
Shared libraries (Material, Ant Design, internal Storybook kits) keep visuals and behavior consistent across products. The architectural win is less duplication and faster alignment with design.
Micro Frontends
Independent teams deploy parts of the same portal (header - team A, catalog - team B). Module Federation, iframes, web components. This is justified when the organization is already split that way; it is expensive in both integration effort and startup performance.
BFF (Backend for Frontend)
A specialized API for a specific client: a mobile BFF returns an entire screen in one aggregated response; a web BFF returns a different shape. That reduces overfetching and hides internal microservice mess from the thin client.
Chapter 14. How databases changed
The SQL era
MySQL, PostgreSQL, Oracle - ACID, JOINs, strict schema. For decades they were the single source of truth for the monolith. In the 2010s and 2020s, PostgreSQL captured a large share of the "general-purpose" database role thanks to JSON support, extensions, and reliability.
The NoSQL revolution
Web 2.0 scale and the desire to avoid rigid schemas in fast-changing products led to:
- MongoDB - documents, flexible schema
- Cassandra - wide writes, fault tolerance
- Redis - cache, sessions, queues, in-memory data structures
CAP theorem and eventual consistency became mandatory parts of an architect's vocabulary.
NewSQL and polyglot persistence
CockroachDB, TiDB - distributed SQL with horizontal scaling. Polyglot Persistence means different stores for different jobs: PostgreSQL for transactions, Elasticsearch for search, Redis for cache, S3 for files. Data architecture became a portfolio, not a single decision for the whole project.
Chapter 15. Observability as part of architecture
Why logs are not enough
In a monolith, a grep through one file often found the cause. In a twenty-service system, a request goes through the gateway, auth, three services, and a queue - without correlation, all you see are twenty unrelated stack traces.
The three pillars of observability
- Logs - discrete events with context (structured logging, JSON).
- Metrics - aggregates over time (RPS, latency p99, error rate).
- Traces - the path of one request through every hop (trace ID in headers).
Tools
Prometheus + Grafana - metrics and dashboards. OpenTelemetry - the instrumentation standard. Jaeger, Tempo - trace visualization. Without these, microservices and serverless systems are blind in production.
Observability has to be designed up front - not added "after the incident."
Chapter 16. Artificial intelligence changes architecture
New requirements
Applications built around LLMs are not just a single HTTP call. They need prompt and model version management, token and cost limits, embedding caches, data privacy controls, and fallback logic when a provider is unavailable.
New components
- Embeddings - vector representations of text for semantic search.
- Retrieval - selecting relevant fragments from a knowledge base.
- RAG (Retrieval-Augmented Generation) - the model answers using your documents, not only its training.
A typical AI application diagram
[Client] -> [API / Orchestrator] -> [LLM API]
↓ ↑
[Vector DB] <- [Embeddings pipeline]
↓
[Document store / CRM / wiki]
The orchestrator decides when to call a tool, when to search the vector database, and when to escalate to a human. Vector stores (pgvector, Pinecone, Qdrant, Weaviate) have become the same kind of architectural choice that Redis was ten years ago.
Chapter 17. Which ideas turned out to be wrong - in context
Not "failed technologies," but misapplied technologies:
Microservices for tiny projects. Three developers do not gain much from independently deploying five services; they gain operational misery.
Excessive distribution. Synchronous chains of seven HTTP calls for one user click; sagas without compensations; "everything in Kafka" with no meaningful consumers.
Premature optimization. Database sharding at one thousand users; multi-region before product-market fit.
Blindly copying Big Tech. Your startup is not Netflix; their solutions are funded by their scale of pain.
The mistake is not the idea of microservices or SPA - it is the mismatch between the idea and the team size, budget, and real load.
Chapter 18. What to expect in the next 10 years
AI-assisted development - agents generate code and infrastructure; the architect focuses on module boundaries and constraints rather than boilerplate.
Edge computing - logic moves closer to the user (Cloudflare Workers, Deno Deploy); less round-trip time, stricter CPU limits.
WebAssembly - heavy computations and ports of existing libraries run in the browser without pushing everything back to the server.
Local AI models - privacy and offline capability; a hybrid model of "small model on device + large model in the cloud when needed."
Agentic systems - chains of autonomous steps with tools; new requirements around security, auditability, and human-in-the-loop workflows.
Self-healing applications - automatic rollback, platform-level canary release strategies, AI-generated remediation runbooks for common incidents.
Predictions will always be fuzzy, but the direction is clear: more automation of routine work, more hybrid models (edge + cloud, local LLM + API, SSR + islands), and more pressure toward simplicity wherever complexity does not pay for itself.
Conclusion
The main lesson of twenty years
Perfect architecture does not exist. Every architecture is a compromise between development speed, total cost of ownership, UX, reliability, and how quickly the business can change. What people call "legacy" today was often the right answer to yesterday's constraints.
How to choose architecture today
- Team size - monolith or modular monolith for a small group; microservices only when coordination pain is already proven.
- Budget - managed services vs your own K8s; the cost of incidents vs the cost of the cloud bill.
- Business needs - a content site, SaaS, marketplace, and real-time product all have different load profiles and SEO requirements.
- Growth trajectory - establish module boundaries early; split into services later, when those boundaries are clear.
Final thought
The most resilient projects are rarely built on the trendiest architecture. They are built on the simplest architecture that solves today's problem and still leaves room to evolve tomorrow. The history of the web is not a race for hype; it is a sequence of sensible responses to changing constraints. Your next stack decision will be the right one if you name those constraints honestly - before you open the README of the next framework.