aboutessaysstudy forgeengineeringcvprojectstimepiecescontact
← Back to Engineering

System Design

Patterns, anti-patterns, and trade-offs in distributed architectures.

Core Patterns

Event-Driven Architecture

Services communicate via events, not direct calls. Decouples producers from consumers. Enables async processing and scales horizontally.

When to use:

  • • Complex workflows with multiple steps
  • • Need for async processing
  • • Multiple consumers of same event
  • • Building for scale

Trade-offs:

  • → Harder to debug (distributed traces required)
  • → Eventual consistency (not immediate)
  • → Event schema versioning complexity

Connections:

→ Neural architecture (transformers process tokens in parallel)

CQRS (Command Query Responsibility Segregation)

Separate read and write models. Optimize each independently. Write model for consistency, read model for performance.

When to use:

  • • Different read/write performance requirements
  • • Complex domain logic on writes
  • • Need multiple views of same data

Trade-offs:

  • → Increased complexity (two models instead of one)
  • → Eventual consistency between read/write
  • → More infrastructure to maintain

Saga Pattern

Distributed transactions via compensating actions. Each service performs local transaction + publishes event. If step fails, run compensations in reverse.

When to use:

  • • Multi-service transactions
  • • Can't use 2PC (two-phase commit)
  • • Need business-level compensation

Trade-offs:

  • → Complex error handling
  • → Compensation logic can be tricky
  • → Not all operations are compensatable

Example:

E-commerce order: Reserve inventory → Charge payment → Ship product. If shipping fails: Refund payment → Release inventory.

Anti-Patterns

Distributed Monolith

Microservices that are tightly coupled. Worst of both worlds: distributed complexity + monolithic coupling.

Symptoms:

  • • Can't deploy services independently
  • • Shared database across services
  • • Synchronous coupling everywhere
  • • Single change requires updating multiple services

Fix:

Go back to monolith OR properly decouple via events + domain boundaries.

Premature Microservices

Starting with microservices before understanding domain boundaries. Results in wrong splits, lots of refactoring.

Better approach:

Start monolith → Identify natural boundaries → Extract services when needed.

"Form before strength." Master the domain before distributing it.

Trade-Off Matrix

PatternComplexityScalabilityConsistency
MonolithLowLimitedStrong
Event-DrivenMediumHighEventual
CQRSHighVery HighEventual
SagaHighHighEventual

No free lunch. Choose trade-offs consciously.

Decision Framework

Start Simple

Monolith until you have clear reason to distribute. Premature distribution is root of much evil.

Split on Domain Boundaries

Services should map to business domains, not technical layers. "User service" not "Database service".

Embrace Eventual Consistency

Strong consistency doesn't scale. Learn to work with eventual. UX patterns can hide it (optimistic updates, loading states).

Design for Failure

Services will fail. Networks will partition. Design for graceful degradation. Circuit breakers, retries, fallbacks.