Engineering Playbook
Code Structure

Design Patterns

Practical GoF patterns for modern development.

Design Patterns

Based on the "Gang of Four" (GoF) book. Many of the original 23 patterns are obsolete in modern languages (like Iterator, which is now built-in), but several remain vital.

Creational Patterns

How objects are created.

Singleton

Ensures a class has only one instance.

Real-World Example: Database Connection Pool

  • Problem: Multiple database connections waste resources
  • Solution: Single connection pool shared across application
  • Implementation: Private constructor, static instance, global access point
  • Benefits: Resource efficiency, consistent state management
  • Drawbacks: Testing difficulties, hidden dependencies, global state

Modern Alternatives:

  • Use Dependency Injection containers
  • Scoped services with controlled lifecycle
  • Environment-specific configurations

Factory Method

Defines an interface for creating an object, but lets subclasses alter the type of objects that will be created.

Real-World Example: Payment Processing System

  • PaymentFactory interface with createPaymentProcessor() method
  • CreditCardFactory creates credit card processors
  • PayPalFactory creates PayPal processors
  • BankTransferFactory creates ACH processors
  • Runtime Selection: Based on user payment method choice

Benefits:

  • Decouples client from concrete classes
  • Easy to add new payment types
  • Follows Open/Closed Principle
  • Centralized creation logic

When to Use:

  • When you can't anticipate class types
  • When subclass delegation is needed
  • When you want to provide users flexibility

Abstract Factory

Provides an interface for creating families of related objects.

Real-World Example: UI Component Library

  • ThemeFactory creates cohesive UI components
  • DarkThemeFactory creates dark buttons, inputs, modals
  • LightThemeFactory creates light variants
  • Consistency: All components match the same theme

Benefits:

  • Ensures product compatibility
  • Isolates client from implementation details
  • Easy to switch entire families

Structural Patterns

How objects are composed.

Adapter

Allows incompatible interfaces to work together.

Real-World Example: Payment Gateway Integration

  • Problem: Stripe and PayPal have different APIs
  • Solution: Create adapters that implement common interface
  • StripeAdapter: Transforms calls to Stripe format
  • PayPalAdapter: Transforms calls to PayPal format
  • Client Code: Works with unified PaymentGateway interface

Implementation Structure:

  1. Define IPaymentGateway interface
  2. Create adapters for each provider
  3. Client uses interface, not concrete classes
  4. Easy to add new payment providers

Benefits:

  • Lets incompatible classes work together
  • Improves reusability of existing code
  • Decouples client from implementation

Facade

Provides a simplified interface to a complex library or framework.

Real-World Example: File Processing System

  • Complex System: File parsers, validators, transformers, storage
  • Facade: FileProcessor with single processFile() method
  • Internal Steps: Parse → Validate → Transform → Store → Notify
  • Client Experience: One method call handles everything

When to Use:

  • Complex subsystems with many dependencies
  • Need for simplified API
  • Layer separation in architecture

Benefits:

  • Hides complexity from clients
  • Reduces coupling between subsystem and client
  • Provides clear entry points

Decorator

Adds new functionality to objects dynamically without altering their structure.

Real-World Example: Coffee Ordering System

  • Base: Simple coffee object
  • Decorators: Milk, Sugar, Vanilla, Whipped cream
  • Dynamic: Mix and match at runtime
  • Pricing: Each decorator adds to base price

Benefits:

  • More flexible than inheritance
  • Add/remove responsibilities at runtime
  • Follows Single Responsibility Principle

Proxy

Provides a surrogate or placeholder for another object to control access to it.

Real-World Example: Image Loading

  • RealImage: Heavy object loads from disk
  • ProxyImage: Lightweight placeholder
  • Lazy Loading: Load real image only when displayed
  • Additional: Caching, access control, logging

Proxy Types:

  • Virtual proxies (lazy loading)
  • Protection proxies (access control)
  • Remote proxies (network access)

Behavioral Patterns

How objects communicate.

Strategy

Enables selecting an algorithm at runtime.

Real-World Example: Navigation App Routing

  • Context: Navigation system
  • Strategies: Fastest route, shortest distance, avoid tolls, scenic route
  • Runtime Selection: User chooses preference
  • Interchangeable: Easy to switch algorithms

Implementation Structure:

  • RouteStrategy interface with calculateRoute() method
  • Concrete strategies for each algorithm
  • Context maintains strategy reference
  • Client can change strategy at runtime

Benefits:

  • Eliminates conditional statements
  • Easier to add new algorithms
  • Follows Open/Closed Principle

Observer (Pub/Sub)

One object notifies others of state changes.

Real-World Example: Stock Market Monitoring

  • Subject: Stock price service
  • Observers: Trading bots, alert systems, dashboard
  • Events: Price changes, volume spikes, market news
  • Independence: Observers operate autonomously

Use Cases:

  • UI event handling (button clicks, form submissions)
  • Real-time data feeds
  • Notification systems
  • Model-View-Controller patterns

Benefits:

  • Loose coupling between subject and observers
  • Dynamic subscription/unsubscription
  • Broadcast communication

Command

Encapsulates a request as an object.

Real-World Example: Restaurant Ordering

  • Command: Order object with all details
  • Invoker: Waiter takes orders
  • Receiver: Kitchen prepares food
  • Features: Queue orders, undo/redo, order history

Benefits:

  • Decouples invoker from receiver
  • Supports undo/redo operations
  • Can queue and log commands
  • Easy to add new commands

Template Method

Defines the skeleton of an algorithm, letting subclasses fill in specific steps.

Real-World Example: Data Processing Pipeline

  • Template: processData() method with fixed steps
  • Steps: Validate → Transform → Save → Notify
  • Variations: Different data formats implement steps differently
  • Invariants: Overall algorithm structure unchanged

Benefits:

  • Code reuse for invariant parts
  • Flexibility for variable parts
  • Control over algorithm structure
  • Follows Hollywood Principle

Iterator

Provides a way to access elements of an aggregate object sequentially without exposing its underlying representation.

Real-World Example: Music Playlist

  • Aggregate: Playlist collection
  • Iterator: Traverses songs in order
  • Variants: Shuffle, repeat, reverse
  • Uniform: Same interface for different playlist types

Modern Relevance:

  • Built into most languages (for...of loops)
  • Still useful for custom collections
  • Foundation for stream processing

Pattern Fever

"When you have a hammer, everything looks like a nail."

Engineers often learn a pattern and try to force it everywhere.

Modern Example: The overuse of useEffect in React. Many developers use it to sync state manually (Observer pattern), causing "Effect Chains" and unnecessary re-renders. often, simple derived state (calculating variables during render) is the cleaner, more performant solution.