Skip to Content
Open to AI related Platform Engineer, DevOps, SRE Roles in Seattle(US) or Vancouver(BC) • Available for onsite/hybrid.
DocumentationCodingOopDesign Patterns Basic

Design Patterns Basic

Design patterns are typical solutions to commonly occurring problems in software design. They are like pre-made blueprints that you can customize to solve a recurring design problem in your code.

https://refactoring.guru/design-patterns/what-is-pattern 

How many design patterns do we have?

  • GoF (Gang of Four) patterns: 23 — the classic, most-cited set.
    • Creational (5), Structural (7), Behavioral (11)

Beyond GoF, people commonly include:

  • Enterprise/architecture patterns (Layered, Hexagonal/Ports & Adapters, CQRS, Event Sourcing, etc.)
  • Concurrency/resilience patterns (Bulkhead, Circuit Breaker, Retry, Rate Limit, etc.)

Most frequently used in Python (in real code + interviews)

1) Strategy

Swap algorithms/policies (pricing, routing, ranking, auth rules).

  • Pythonic form: pass a callable, or use a small class with __call__.

2) Factory (often “Simple Factory”)

Centralize object creation logic (based on config, type, environment).

  • Pythonic: dict/registry mapping → constructor.

3) Adapter

Wrap external libraries/APIs into your app’s interface (DB client, HTTP client).

  • Super common in services.

4) Decorator

Add cross-cutting behavior: caching, retries, metrics, auth checks.

  • Python has built-in decorator syntax, so this shows up everywhere.

5) Observer / Pub-Sub

Events and handlers (domain events, message buses, signals).

  • Pythonic: callbacks, event emitters, queues.

6) Facade

Expose a simplified interface over a messy subsystem (SDK wrapper, “client” class).

7) Template Method (used, but carefully)

Base workflow + hook methods. In Python, often replaced by composition/callables, but still appears.

Python modules already behave like singletons; explicit Singleton is usually a smell.

  • Interview-safe phrasing: “Prefer dependency injection; use module-level instance only when justified.”

Staff Engineer Notes: Design Patterns in CPython & the Python Standard Library (with real source refs)

When you interview at senior/staff level, “design patterns” isn’t about memorizing GoF names—it’s about recognizing stable abstractions the runtime/stdlib uses to keep code extensible, testable, and portable.

Below are battle-tested patterns you can cite directly from CPython/stdlib.

1) Iterator Pattern

What it looks like in Python

Anything you can loop over is using the iterator protocol (__iter__, __next__).

Why staff engineers care

  • Enables streaming processing (ETL/pipelines).
  • Avoids loading full datasets into memory.

Where to reference

  • io module overview describes file objects/streams and the standard I/O abstractions around them. (Python documentation )

2) Context Manager Pattern

What it is

A standardized “resource lifecycle” protocol: enter → exit (with exception safety).

Why it matters

  • Correct cleanup for resources (files, locks, DB sessions, temp dirs).
  • Composable cleanup when you don’t know the resources at compile time.

Key stdlib example: contextlib.ExitStack

ExitStack is designed to manage multiple context managers programmatically and unwind in reverse order. (Python documentation )


3) Factory Pattern

Example: pathlib.Path is an OS-aware factory

Instantiating Path(...) yields either a PosixPath or WindowsPath depending on the platform. (Python documentation )

Why this is a factory: You request “a Path,” and the library chooses the right concrete implementation for you.

Example: logging.getLogger(name) returns a cached logger

Multiple calls with the same name return the same logger object (factory + registry/cache behavior). (Python documentation )


4) Strategy Pattern

What it looks like in Python for SP

You inject behavior as a function/object (policy) rather than branching everywhere.

Canonical stdlib pattern

functools.lru_cache / functools.cache changes performance behavior by applying a caching strategy around your function. (Python documentation )

Staff angle: “Strategy” in Python is often just “pass a callable” or “decorate the callable.”


5) Observer / Publish–Subscribe Pattern

Example A: logging handlers

logging is explicitly a flexible event logging system; loggers emit events and handlers process them (subscriber chain). (Python documentation )

Example B: asyncio.Future.add_done_callback

Callbacks registered via add_done_callback() are invoked when the Future completes (and scheduled on the loop). (Python documentation )

Staff angle (distributed workers):

  • Futures + callbacks are a core building block for composing async workflows without tight coupling.

6) Adapter Pattern

Example: io.TextIOWrapper adapts byte streams to text streams

The subprocess docs explicitly mention using io.TextIOWrapper defaults when operating in text mode, reflecting the “layered” stream design. (Python documentation )

Why it’s an adapter: it bridges a binary buffer to a text interface (encoding/decoding layer). (See io stream categories for the model.) (Python documentation )


7) Facade Pattern

The subprocess docs say the recommended approach is to use run() for use cases it can handle, falling back to Popen for advanced use. (Python documentation )

Why it’s a facade: it hides the complexity of process creation/IO/waiting behind a simpler function that returns a CompletedProcess.


8) Template Method Pattern

Example: unittest.TestCase lifecycle

TestCase defines a stable execution skeleton where setUp() and tearDown() run per test (the algorithm is fixed; you override steps). (Python documentation )

Staff angle: This is how frameworks keep the workflow stable while letting you customize the hooks.


9) Singleton (Pythonic form)

Example: module caching via sys.modules

sys.modules maps module names to already loaded modules and is central to import caching behavior. (Python documentation )

Staff nuance: In Python, “singleton” is often just “module-level instance + import cache,” and explicit singleton classes are usually unnecessary.


10) Abstract Base Classes as Contract Boundaries (DIP-friendly)

Example: importlib.abc

importlib.abc contains the core abstract base classes that define the import system interfaces. (Python documentation )

Why staff engineers care: this is Dependency Inversion in practice—core logic depends on stable ABC contracts while implementations vary.

How to use these in ETL & Distributed Worker interviews (quick talk track)

  • Iterator + Context Manager → streaming ETL with safe cleanup (files, DB cursors).
  • Strategy + Decorator (lru_cache) → policy injection + performance control without touching core logic. (Python documentation )
  • Observer (logging, Future callbacks) → decoupled event handling and async composition. (Python documentation )
  • Facade (subprocess.run) → simple interfaces for common operations; drop down to advanced API only when needed. (Python documentation )
  • ABC contracts (importlib.abc) → clean extension points and DIP boundaries. (Python documentation )
Last updated on