Principles
27 documents
Composition over inheritance
Default to composing behaviors from small, focused pieces. Use inheritance only for genuine 'is-a' relationships, and...
Connascence
Coupling has a rankable strength; a good refactor moves it to a weaker form, and weaker still as distance grows.
Conway's Law
A system's architecture mirrors the communication structure of the teams or agents that build it — so shape those boundaries to match the design you want.
Deliberate, prudent technical debt
Trading rigor for speed is legitimate only when the choice is deliberate, recorded, and has a payback trigger.
Dependency injection
A component should receive its dependencies from the outside, not construct them internally:
Design for deletion
Every line of code is a maintenance liability. Build disposable software, not reusable software:
DRY
Every piece of knowledge should have a single, authoritative representation. DRY is a rule about knowledge, not about code shape.
Errors as values
Represent expected, recoverable failures as values so failure paths show up in signatures and must be handled.
Explicit over implicit
Hidden behavior, magic, and implicit coupling create bugs that take days to find:
Fail fast
Invalid state should be detected and surfaced immediately at the point of origin, not propagated silently:
For novel components, prefer proven open-source solutions
When no native solution exists, research battle-tested open-source libraries and present options to the user before b...
Idempotency
User actions and system operations should be safe to repeat without duplicate side effects:
Immutability by default
Mutable shared state is the root cause of most concurrency bugs. Default to immutable values; introduce mutability on...
Make illegal states unrepresentable
Encode invariants in types and parse untrusted input once into a value that carries proof of its own validity.
Make It Work, Make It Right, Make It Fast
Separate correctness, design quality, and performance into sequential phases:
Manage complexity through boundaries
Well-defined boundaries between subsystems let each side evolve independently. Define ports (interfaces) that describ...
Meta-Principle: Optimize for Change
Every principle above is a strategy for making future change cheaper and safer. When evaluating any technical decisio...
Prefer native controls and libraries
Always use the platform's built-in frameworks before custom implementations. Swift Concurrency over raw threads. Room...
Principle of least astonishment
APIs, UI, and system behavior should match what users and callers expect. If a name suggests one behavior, it must de...
Separation of concerns
A module should have one reason to change. If describing what a module does requires 'and,' consider splitting. This ...
Simplicity
Simple and easy are not synonyms. Simple means no interleaving of concerns. Easy means familiar or convenient. Optimi...
Small, reversible decisions
If a decision is cheap to reverse, make it fast. If it is expensive to reverse, invest in understanding first:
SRP
A module should be answerable to one and only one actor. Reason-to-change is a stakeholder, not a concern.
Steel thread first
Build the thinnest end-to-end slice through every boundary before breadth — the disciplined answer to scaffolding vs YAGNI.
Support Automation
Applications should expose their capabilities through automation interfaces, not just interactive UI.
Tight feedback loops
The speed of your feedback loop is the speed of your learning:
YAGNI
Build for today's known requirements. Speculative generality adds code that must be maintained but delivers no curren...