Design Patterns
Design patterns are named boundaries for recurring design problems. The useful part is not the name; it is the dependency direction the pattern creates. Strategy separates interchangeable algorithms, Adapter hides incompatible interfaces, Factory centralizes construction, Repository isolates persistence, and Facade gives callers a smaller API over a complex subsystem.
Patterns in data and AI code
In data and AI products, Strategy is common when the same request path may use BM25, dense retrieval, hybrid retrieval, or a fallback policy. Instead of scattering if ranker == ... across a web backend, define one scoring interface and inject the implementation. That keeps testing focused on behavior and keeps technical decision records honest about why an implementation was selected.
Strategy as a scorer interface
This snippet implements a scorer strategy interface and swaps keyword and freshness scorers without changing the ranking function.
from dataclasses import dataclass
from typing import Protocol
@dataclass(frozen=True)
class Document:
id: str
text: str
age_days: int
class Scorer(Protocol):
def score(self, query: str, doc: Document) -> float: ...
class KeywordScorer:
def score(self, query, doc):
return sum(doc.text.lower().count(t) for t in query.lower().split())
class FreshnessScorer:
def score(self, query, doc):
return 1 / (1 + doc.age_days)
def rank(query, docs, scorer: Scorer):
return [d.id for d in sorted(docs, key=lambda d: scorer.score(query, d), reverse=True)]
docs = [Document("A", "refund refund policy", 9), Document("B", "outage summary", 0)]
print("keyword", rank("refund", docs, KeywordScorer()))
print("freshness", rank("refund", docs, FreshnessScorer()))Observed output:
keyword ['A', 'B']
freshness ['B', 'A']The caller does not change when the scoring policy changes. That is a real reduction in coupling. If the project has only one scorer and no planned alternative, the interface may be premature; refactoring can introduce it later when duplication appears.
Failure modes
Pattern misuse adds vocabulary without reducing complexity. Warning signs are interfaces with one implementation, factories that hide simple constructors, inheritance trees used where composition would work, and architecture diagrams that say “adapter” but still leak provider-specific fields into API design.
References
Nav