Building Scalable Web Applications

From the archive: a general guide from March 2024, tidied and kept for reference.

Building Scalable Web Applications

Introduction

A web application that scales takes some planning, a sound architecture and a handful of well-known practices. This guide covers the main patterns and principles that let an application handle more load as it grows.

Architecture Patterns

A common pattern for larger applications is to split them into small services, each owning its own data. The sketch below shows the idea; it is a service map, not a real configuration format.

Microservices Architecture


services:
  auth-service:
    port: 3001
    dependencies:
      - auth-db
  user-service:
    port: 3002
    dependencies:
      - user-db
  product-service:
    port: 3003
    dependencies:
      - product-db

Key Benefits

  • Independent scaling of services
  • Technology flexibility
  • Improved fault isolation
  • Better team organization

Scalability Principles

Horizontal vs Vertical Scaling

Horizontal Scaling
  • Add more machines
  • Distribute load
  • Improved redundancy
Vertical Scaling
  • Upgrade existing hardware
  • Increase resources
  • Simpler implementation

// Node.js cluster: one worker process per CPU core
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;

if (cluster.isPrimary) {
    // Fork workers for each CPU core
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }
} else {
    // Workers share the TCP connection
    require('./app');
}

Performance Optimization

Caching Strategies


// Redis caching example (node-redis v4)
const getUser = async (userId) => {
    // Try cache first
    const cachedUser = await redis.get(`user:${userId}`);
    if (cachedUser) return JSON.parse(cachedUser);

    // Database query if cache miss
    const user = await db.users.findById(userId);
    // Expire after 5 minutes so stale data does not live forever
    await redis.set(`user:${userId}`, JSON.stringify(user), { EX: 300 });
    return user;
};

Database Optimization

  • Index optimization
  • Query performance tuning
  • Connection pooling
  • Sharding strategies

Infrastructure Design

Cloud Infrastructure Components

  • Load Balancers
  • Auto-scaling Groups
  • CDN Integration
  • Database Clusters
  • Message Queues

# Docker Compose example (compose.yaml)
services:
  app:
    build: .
    deploy:
      replicas: 3
    depends_on:
      - redis
      - postgres
  redis:
    image: redis:alpine
  postgres:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

Monitoring and Maintenance

Key Metrics to Monitor

  • Response Time
  • Error Rates
  • Resource Utilization
  • User Experience

Tools and Services

  • Prometheus & Grafana
  • ELK Stack
  • New Relic
  • Datadog