Lesson 0017 · System Designer · Module 1
Protocols and ABCs: Clean Extension Points
An extension point is a promise: "anything with these methods works here." Protocols make the promise structural; ABCs make it nominal. Choose by who's making the promise.
Mission tie-in: "use protocols and abstract base classes to design clean extension points" — how the System Designer opens a module for extension without weakening its core.
Knowledge: two ways to promise
Protocol — structural typing: an object qualifies by having the methods, whether or not it ever heard of your interface. The extension point is open to anything that walks like the duck:
from typing import Protocol, runtime_checkable
@runtime_checkable
class Notifier(Protocol):
def send(self, message: str, recipient: str) -> None: ...
def notify_all(notifier: Notifier, users: list[User]) -> None:
for user in users:
notifier.send(f"Update for {user.name}", user.email)
ABC — nominal typing: an object qualifies by declaring itself a subclass, and the base can provide default behavior and enforce method presence:
from abc import ABC, abstractmethod
class Notifier(ABC):
@abstractmethod
def send(self, message: str, recipient: str) -> None: ...
def send_bulk(self, messages: list[str], recipient: str) -> None: # shared behavior
for m in messages:
self.send(m, recipient)
How to choose:
- Protocol, when the module must accept things it doesn't own — third-party objects, framework callbacks, your own legacy code. The promise is "if you have these methods, you're in."
- ABC, when you own the family and want shared default behavior, enforced structure, or a place where the extension contract is discovered by subclassing.
Either way, the extension point should be deep (lesson 0001's reading): a small, stable promise that hides how much work it enables. If the interface is bigger than the implementation, it's not an extension point — it's a leak.
† AI-generated code tends to reach forABC by default (it's the famous one). Review it: if nothing shares default behavior and nothing declares ancestry, a Protocol (or a bare callable, lesson 0016) was lighter and just as open.
| Your situation | Choose | Because |
|---|---|---|
| you must accept objects you don’t own | Protocol | Third-party classes will never subclass your base. |
| the variants share a real default step | ABC | The base is the only place that step can live once. |
| one method, no state, no family | a plain callable | The interface would add a name and nothing else. |
| you need a runtime isinstance check | ABC (or @runtime_checkable) | Structural checks verify method names, not signatures. |
| eight methods, implementations use three | neither, yet | The promise is bigger than the need — split it first. |
Skill: which promise fits?
Your module must accept any object with a render() method, even ones you don't own. Choose:
You own a family of parsers sharing one default step. Choose:
An interface bigger than its implementations is a sign of:
Practice on your own code
Find an ABC in your codebase (or an AI-generated one). Ask: do the subclasses share default behavior, or do they each implement everything? If the ABC is just a contract with no shared behavior and no external subclasses, convert it to a Protocol.
Reveal: an ABC that was really a protocol
# an AI wrote this: a contract with zero shared behavior
class Exporter(ABC):
@abstractmethod
def export(self, data) -> str: ...
# every subclass implemented export() fully; nothing shared; nothing external
# convert to a protocol — same contract, open to anything with export()
class Exporter(Protocol):
def export(self, data) -> str: ...
Your win
You can now pick the extension-point mechanism by ownership and shared behavior — protocol for open, ABC for family — and you know the leak test for the interface's size.
Read and watch deeper
- Fluent Python, Ramalho — ch. 13 "Protocols and ABCs": the definitive Python treatment of the choice.
- Python docs — typing.Protocol: the structural-typing reference.
- A Philosophy of Software Design, Ousterhout — ch. 4: deep modules as the shape of good interfaces.
- Watch: ArjanCodes YouTube — search "protocol or abstract base class".
- Reference: Glossary — protocol; and Design Principles — open–closed, the principle an extension point serves.
Show your agent-teacher an AI-generated ABC and argue the protocol conversion together.