Methodology8 min

Scaling Right: when and how to grow without waste

Scaling isn't just adding more servers. Learn to identify the right moment and correct strategy for growth.

"The system is slow, let's add more instances." A week later: "Still slow, but now it costs twice as much." Scaling without understanding the bottleneck is throwing money away. This article teaches you to scale right — at the right time, in the right way.

Scaling is the last option, not the first. Optimizing is cheaper.

When to Scale

Signs you need to scale

Legitimate indicators:
  - Resources at >80% sustained utilization
  - Optimizations already done
  - Load growth is inevitable
  - Headroom for spikes is insufficient

Indicators you DON'T need to scale:
  - High latency but idle resources
  - Performance degraded after code change
  - Bottleneck in specific component
  - Intermittent problem

Questions before scaling

1. Is the bottleneck capacity or code?
   → Did profiling/tracing identify where time is spent?

2. Have obvious optimizations been done?
   → DB indexes? Cache? Connection pooling?

3. Will scaling solve or move the bottleneck?
   → More app servers don't help if DB is bottleneck

4. What's the cost of scaling vs optimizing?
   → 2 extra instances = $X/month
   → 1 week of optimization = $Y one-time

5. Horizontal or vertical scaling?
   → Depends on load type and architecture

Scaling Strategies

Vertical Scaling (Scale Up)

What it is:
  Increase resources of one instance
  (CPU, memory, disk)

When to use:
  - Application isn't distributed
  - Coordination overhead is high
  - Limit not yet reached
  - Simple to implement

Advantages:
  - Doesn't require code change
  - No distribution complexity
  - Minimal latency between components

Disadvantages:
  - Physical limit (largest available instance)
  - Cost grows non-linearly
  - Single point of failure

Horizontal Scaling (Scale Out)

What it is:
  Add more instances
  with load balancer distributing load

When to use:
  - Application is stateless
  - Load is parallelizable
  - Need high availability
  - Vertical limit already reached

Advantages:
  - Theoretically unlimited
  - Linear cost
  - Natural redundancy

Disadvantages:
  - Requires adequate architecture
  - Coordination complexity
  - State management is a challenge

Component Scaling

Don't scale everything equally:

Identify bottleneck:
  - App servers: CPU-bound
  - Database: I/O-bound
  - Cache: Memory-bound

Scale selectively:
  If bottleneck = App:
    → Add more pods/instances

  If bottleneck = DB reads:
    → Add read replicas

  If bottleneck = DB writes:
    → Sharding or different database

  If bottleneck = Cache:
    → Increase memory or cluster

The Cost of Scaling Wrong

Scenario 1: Scale when should optimize

Problem: High latency (2s)
Action: Double number of servers
Cost: +$5K/month

Result:
  - Latency still 2s
  - Bottleneck was query without index
  - Correct solution: CREATE INDEX (5 min, $0)

Waste: $5K/month × 12 = $60K/year

Scenario 2: Scale wrong component

Problem: Checkout slow under load
Action: Triple application servers
Cost: +$10K/month

Result:
  - Checkout still slow
  - Bottleneck was DB connection pool
  - DB doesn't scale with more app servers

Correct solution:
  - Increase connection pool
  - Or add read replica for queries

Scenario 3: Scale before needed

Problem: "Black Friday is coming"
Action: Provision 10x current capacity
Cost: +$50K/month

Result:
  - Black Friday had 3x peak (not 10x)
  - Most resources idle
  - Money wasted

Correct solution:
  - Stress test to validate real need
  - Scale for safety margin (20-50%)
  - Autoscaling for spikes

Capacity Planning

Projection model

Historical data:
  - Monthly growth: 15%
  - Current peak: 1000 req/s
  - Current capacity: 1500 req/s

Projection:
  Month 3: 1520 req/s (reaches limit)
  Month 6: 2313 req/s (50% above limit)

Action:
  Plan scaling for month 2
  Validate with stress test in month 1

Safety margin

Rule of thumb:
  Capacity = Expected peak × 1.5

Example:
  Expected Black Friday peak: 5000 req/s
  Needed capacity: 7500 req/s
  Current capacity: 3000 req/s
  Gap: 4500 req/s

Plan:
  - Option A: Scale to 8000 req/s (+$X)
  - Option B: Optimize to 5000 req/s (X sprints)
  - Option C: Combination

Architecture for Scale

Principles

Stateless:
  - Session in Redis, not memory
  - Files in S3, not local disk
  - Allows horizontal scaling

Loose Coupling:
  - Async communication where possible
  - Queues to absorb spikes
  - One's failure doesn't bring others down

Database:
  - Read replicas for reads
  - Adequate connection pooling
  - Indexes for critical queries

Cache:
  - Aggressive caching of static data
  - Well-defined invalidation
  - Multi-layer cache

Scaling patterns

Load Balancer:
  - Distributes load among instances
  - Health checks to remove unhealthy
  - Sticky sessions if needed (avoid)

Auto-scaling:
  - Scales based on metrics
  - CPU, memory, queue depth, custom
  - Scale-in policy for savings

Queue-based:
  - Absorbs load spikes
  - Allows processing at possible rate
  - Decouples producers from consumers

CDN:
  - Offloads static assets
  - Edge caching for latency
  - DDoS protection

Autoscaling

When to use

Good for:
  - Variable and predictable load
  - Stateless application
  - Fast startup time
  - Cost matters

Bad for:
  - Always constant load
  - Slow startup (>5 min)
  - Local state required

Typical configuration

# Kubernetes HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-scaler
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 3
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Pods
    pods:
      metric:
        name: requests_per_second
      target:
        type: AverageValue
        averageValue: 1000
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300

Autoscaling metrics

Common metrics:
  - CPU utilization (most common)
  - Memory utilization
  - Request rate (req/s)
  - Queue depth
  - Custom metrics (latency, etc)

Cautions:
  - Scale-up fast to react to spikes
  - Scale-down slow to avoid flapping
  - Margin for cold start
  - Sensible min/max limits

Validating Scale

Before production

1. Stress test at new size:
   - Validates it actually solves
   - Identifies new bottleneck

2. Chaos testing:
   - Fail one instance
   - Validate others absorb

3. Cost validated:
   - Monthly cost projection
   - Budget approval

After production

1. Monitor utilization:
   - Are resources being used?
   - Or idle?

2. Review periodically:
   - Does capacity still make sense?
   - Can reduce without risk?

3. Adjust autoscaling:
   - Adequate thresholds?
   - Reaction time ok?

Conclusion

Scaling right means:

  1. Identify bottleneck first - scale the right component
  2. Optimize before scaling - cheaper and sustainable
  3. Choose correct strategy - vertical, horizontal, or component
  4. Plan with data - not with fear
  5. Validate before production - stress test confirms
  6. Review after production - adjust or reduce

The question isn't "how much to scale", but "why scale" and "scale what".

More hardware doesn't fix bad code. It just fixes the cloud bill.


This article is part of the series on the OCTOPUS Performance Engineering methodology.

OCTOPUSscalabilitycapacityarchitecture

Want to understand your platform's limits?

Contact us for a performance assessment.

Contact Us