Architecture Principles
This is a living document. It evolves as systems evolve.
These principles shape every article on this blog. They're not rulesâthey're constraints I've chosen intentionally based on what survives production.
1. Prefer Event-Driven Over Polling
Polling asks "has something happened yet?" every N seconds. Events say "it just happened" exactly once.
Time-based systems are predictable. Event-driven systems are responsive.
Use cron when time is the trigger. Nightly reports, hourly cleanups, scheduled maintenanceâthese depend on the clock, not on state changes.
Use events when change is the trigger. User registers, payment completes, inventory updatesâthese should propagate immediately, not wait for the next poll interval.
If your system checks every minute "just in case," you're compensating for missing architecture. Polling increases load. Events increase clarity.
2. Optimize for Cognitive Clarity
A system that works but cannot be understood is already broken.
Clarity scales. Cleverness does not.
Reduce:
- Mental overhead (how much context you need to hold)
- Hidden coupling (implicit dependencies between components)
- Implicit behavior (magic that happens without calling it)
- Surprising side effects (mutations you didn't expect)
If a new engineer needs a diagram and a meeting to understand a feature, the system is too complex. Readable systems outlive smart systems.
3. Design for Change, Not Perfection
Perfection is static. Software is not.
Architectural elegance that cannot adapt becomes technical debt. The best design today will need modification tomorrow.
Design for:
- Extensibility over rigidity â can you add without rewriting?
- Reversibility over finality â can you back out if this was wrong?
- Isolation of responsibilities â does each part do one thing well?
- Clear boundaries â where does this end and that begin?
The question is not "is this perfect?" The question is "can this evolve safely?"
4. Every Pattern Has a Cost
Queues introduce latency. Events introduce complexity. Cron introduces waste. Abstractions introduce indirection. Microservices introduce coordination overhead.
There is no free architecture.
Every design decision is a trade-off between clarity, scalability, and operational cost. The pattern that solved one problem just created three new ones. Make those trade-offs explicit.
Document why you chose the expensive option. Future engineers (including you) will thank you when they understand what you were optimizing for.
5. Simplicity Is a Strategic Choice
Simplicity is not minimalism for its own sake. It's the intentional reduction of accidental complexityâthe complexity that serves the implementation, not the domain.
Simple systems:
- Fail predictably (you know what broke)
- Scale incrementally (add capacity, not rewrites)
- Are easier to debug (fewer places to look)
- Require fewer meetings (less coordination overhead)
Simplicity is not lack of ambition. It's disciplined focus on what actually matters.
6. Avoid Accidental Distributed Systems
If your feature suddenly requires:
- Retries and exponential backoff
- Dead-letter queues
- Cross-service coordination
- Eventual consistency guarantees
You are no longer building a feature. You are building distributed infrastructure.
Sometimes that's necessaryâmicroservices, event sourcing, CQRS all have legitimate use cases. Often it's accidentalâyou reached for a pattern because it sounded sophisticated, not because the problem demanded it.
Architect consciously. Distributed systems are not free.
The Purpose of These Principles
These principles are not about style. They're about longevity.
Software should survive:
- New requirements (the product will change)
- New engineers (the team will turn over)
- New infrastructure (the platform will evolve)
- Time itself (you'll read this code in two years and need to understand it)
The goal is not to build impressive systems. The goal is to build systems that still make sense when everything around them has changed.

