Chapter 3 – Interview Questions: Web Architecture Patterns
Practice answering these out loud before revealing the sample answers.
Q1: How would you scale a web app that's getting slow?
First thing I'd do is figure out where it's slow. Is it the database? The application servers? The network? You can't fix what you haven't measured. I'd look at metrics — CPU, memory, request latency percentiles, database query times.
Once I know the bottleneck, I'd address it specifically. If the database is the problem, I'd look at query optimization, adding indexes, read replicas, or caching hot data in Redis. If it's the app servers being overwhelmed, I'd add more instances behind a load balancer — that's horizontal scaling. I'd also look for quick wins like adding a CDN for static assets, enabling response compression, and caching API responses that don't change frequently.
The way I think about it is: scaling is detective work first, engineering second. The worst thing you can do is throw hardware at a problem that's actually a missing database index.
Key Points:
- Starts with measurement, not assumptions
- Identifies specific bottleneck before applying solutions
- Mentions both vertical optimizations and horizontal scaling
- Shows cost-awareness (quick wins first)
Follow-ups: How do you decide between caching and scaling? At what point would you consider re-architecting vs. scaling what you have?
Q2: What's the difference between stateful and stateless, and why does it matter?
So basically, a stateless service doesn't remember anything between requests. Every request contains everything the server needs to process it. A stateful service, on the other hand, keeps some context — like a WebSocket connection or an in-memory session.
Why does this matter? Stateless services are dramatically easier to scale. If any server can handle any request, you just add more servers and put a load balancer in front. Done. With stateful services, you've got sticky sessions, you've got the problem of what happens when that specific server dies — all that state is gone.
The way I think about it is: push state out of your application servers and into dedicated stores — a database, Redis, S3, whatever. Keep your compute layer stateless so you can scale it elastically. The state still exists, but it lives somewhere purpose-built for durability and sharing.
Key Points:
- Clear, concise definition of both concepts
- Connects statelessness directly to scalability
- Explains the failure mode of stateful services
- Offers the practical pattern: externalize state
Follow-ups: Are there cases where stateful is the right choice? How do you handle WebSocket connections in a stateless architecture?
Q3: When would you choose vertical over horizontal scaling?
Honestly, vertical scaling gets a bad rap, but it's often the right first move. If you're running a single Postgres database and it's struggling, sometimes the smartest thing is to just give it more RAM and faster SSDs. It's simple, it works, and you can do it in an afternoon without re-architecting anything.
I'd choose vertical when: the system is inherently hard to distribute (like a relational database with complex transactions), when you haven't hit the ceiling of a single machine yet, or when engineering time is more expensive than hardware. A beefy machine with 256GB of RAM can handle a lot of traffic.
I'd switch to horizontal when: you've hit the limits of a single machine, you need fault tolerance (one big machine is a single point of failure), or your workload is embarrassingly parallel — like stateless web servers handling independent requests. The way I think about it is: vertical buys you time, horizontal buys you scale.
Key Points:
- Doesn't dismiss vertical scaling as "wrong"
- Gives clear criteria for choosing each approach
- Acknowledges operational complexity of horizontal
- Understands that some systems resist horizontal scaling
Follow-ups: What's the cost trade-off between vertical and horizontal at cloud prices? How do you scale a database horizontally?
Q4: How do you handle sessions in a load-balanced environment?
There are a few approaches and honestly the right one depends on your constraints. The simplest is sticky sessions — the load balancer routes a user to the same server every time, usually via a cookie. It works but it's fragile — if that server goes down, the user loses their session.
What I'd typically recommend is an external session store — something like Redis or Memcached. Every server can read and write sessions to this shared store, so it doesn't matter which server handles the request. This gives you true stateless app servers that you can scale freely.
The third option, which I prefer for most modern apps, is client-side tokens — JWTs or signed cookies. The session data lives in the token itself, so the server doesn't need to look anything up. The trade-off is you can't easily invalidate a token before it expires, so you might still need a small server-side blacklist for things like logout.
Key Points:
- Knows multiple strategies and their trade-offs
- Explains why sticky sessions are fragile
- Recommends external store or tokens with reasoning
- Acknowledges the JWT invalidation problem
Follow-ups: How do you handle session expiration and renewal? What are the security implications of storing session data in a JWT?
Q5: Describe the evolution of a web architecture as it grows.
I love this question because it mirrors what I've actually lived through. Stage one: you start with a single server — your app, your database, maybe even your static files, all on one box. Simple, fast to develop, totally fine for early days.
Stage two: you separate concerns. Database gets its own server. You add a CDN for static assets. Maybe a reverse proxy like Nginx in front. Stage three: you go horizontal — multiple app servers behind a load balancer, read replicas for the database, a caching layer like Redis.
Stage four: as you grow further, you start breaking the monolith into services. Not microservices necessarily — maybe just separating the auth system, the payment processing, the notification service. You add message queues for async work. Stage five: you're dealing with global scale — multi-region deployments, database sharding, event-driven architecture, eventual consistency trade-offs.
The key insight, honestly, is that you don't jump to stage five on day one. Each stage is the right architecture for that level of scale. Premature complexity kills more startups than scaling problems do.
Key Points:
- Shows a clear progression with reasoning at each stage
- Doesn't skip steps or jump to over-engineered solutions
- Mentions specific technologies at each tier
- Ends with the wisdom that complexity should be earned
Follow-ups: How do you know it's time to move to the next stage? What's the hardest transition you've experienced?