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...

kotlinswift

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...

swift

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...