M03 summary and Part 1 wrap-up
The core ideas on decisions and abstractions, review questions and an audit covering all of Part 1.
What to take from this module
Section titled “What to take from this module”- Every technical choice trades one cost for another. If an option seems to have no downside, you don’t understand it yet.
- Classify decisions by reversibility. Two-way doors: decide fast. One-way doors: investigate and record.
- A short ADR, with context in numbers, alternatives, negative consequences and a trigger to revisit, is usually worth more than extensive documentation.
- Complexity is a recurring cost. With AI, writing got cheap and maintaining didn’t. The bar has to be stricter.
- Design for 10× your current volume, not 1000×. Use boring technology and spend innovation tokens only where your edge is.
- Don’t postpone the fundamentals: constraints, tested backups, auth, idempotency, secrets, alerts, migrations and rollback.
- Duplication is cheaper than the wrong abstraction. Wait for three cases, share mechanism (not policy) and isolate external boundaries.
Review questions
Section titled “Review questions”Give an example of a one-way door and a two-way door in your system. How much time does each deserve?
One-way: the main data model, the public ID format or an API contract used by third parties. These deserve a prototype, discussion and an ADR. Two-way: the validation library, the lint tool, an email provider isolated in a module. These deserve a quick decision with enough information.
Which part of an ADR is most often forgotten, and why does it matter?
The trigger to revisit. It gives a static decision an expiry date: it says under what condition (volume, team size, cost) the decision stops applying, and it guards against both premature change and forgotten decisions.
Your function takes the parameters isAdmin, skipValidation and legacyMode. What does that indicate, and how do you refactor it?
It indicates several functions disguised as one, with special cases piling up. The refactoring is to reintroduce duplication: write an explicit function for each use case, remove the branches that don’t apply to each one, and only then extract the mechanism that’s truly common.
Why is it worth creating an interface for the email provider even with a single implementation?
Because it’s a boundary with something you don’t control. The interface, defined by what your app needs, keeps the SDK in a single file. Switching providers, faking it in tests and translating errors all become local changes. For internal code, an interface with a single implementation is usually just indirection.
A colleague proposes Kubernetes for a system with 300 users and one developer. What questions do you ask?
What problem does this solve today? What happens if we do it six months from now? Who will run it and be on call? What’s the simplest path that handles 10× the current volume? The answer almost always points to a simpler managed platform.
Next step
Section titled “Next step”Part 2 gets into everyday groundwork: Git as an investigation tool, an AI workflow that doesn’t pile up debt, how code actually runs (event loop, memory), algorithmic complexity, the frontend and dependencies.
Go deeper
Materials in English unless noted.
- BookA Philosophy of Software DesignJohn Ousterhout
Complexity, deep modules and good interfaces. The best short book on the topic of M03.
- Article