Designing Defaults That Fail Safely
A practical reflection on choosing defaults that protect users and systems when configuration, rollout, or external dependency behavior is uncertain.
Writing
How to structure source code so it survives growth, teams, and change.
Practical, beginner-friendly deep-dives on software architecture and how source code is organised — from Ports & Adapters and Clean Architecture to dependency management, module boundaries, and the trade-offs behind each. Built on diagrams, real examples, and lessons that scale from solo projects to enterprise systems.
A practical reflection on choosing defaults that protect users and systems when configuration, rollout, or external dependency behavior is uncertain.
A practical reflection on designing data contracts that are narrow, testable, and stable enough for teams to rely on.
A practical reflection on choosing simple software boundaries that make ownership, testing, and future change easier to reason about.
A practical reflection on building safe operational escape hatches that are explicit, audited, and bounded instead of hidden back doors.
A practical reflection on designing interfaces that show real system limits before users and operators have to discover them through failure.
A practical reflection on designing software around visible change patterns instead of abstract future flexibility that may never arrive.
A practical reflection on why software boundaries become clearer when teams anchor them in examples, responsibilities, and behavior instead of only naming layers.
A practical reflection on making architecture decisions small, explicit, and revisitable so teams can learn without turning every choice into a permanent monument.
A practical reflection on designing software interfaces that keep product behavior stable while implementations change underneath.
A practical reflection on refactoring toward a clearer core: how teams can reduce coupling, protect behavior, and make the center of a codebase easier for future teammates to understand.
A practical reflection on architecture decisions as team memory: how small ADR-style notes help future teammates understand trade-offs, rejected paths, and when to revisit a choice.
A practical reflection on architecture boundaries: why teams should name ownership, data, and change patterns before reaching for frameworks, services, queues, or databases.
A practical explanation of observability as an architecture decision: why teams should design logs, metrics, traces, dashboards, and alerts into the system before production asks hard questions.
A practical explanation of the Outbox Pattern: why dual writes fail, how a transactional outbox keeps database state and events aligned, and what teams still need to handle around retries, idempotency, ordering, cleanup, and observability.
A practical explanation of service mesh trade-offs: what it centralizes, when it helps, and why teams should solve real service-to-service pain before adopting another infrastructure layer.
A beginner-friendly guide to Event Sourcing: storing facts as an append-only event history, rebuilding current state through projections, and understanding when the pattern is worth its operational cost.
A practical explanation of the Saga pattern for distributed transactions: why one database transaction stops working across services, how orchestration and choreography differ, and why compensation, observability, and idempotency matter.
A practical look at API gateway responsibilities: routing, authentication boundaries, rate limiting, request aggregation, backend-for-frontend trade-offs, failure modes, and the observability needed to keep the edge of a system understandable.
A practical guide to applying Hexagonal Architecture to one small feature: drawing the boundary, naming ports in business language, keeping adapters at the edge, testing the core, and deciding when the extra structure is worth the cost.
When services hold hands through synchronous calls, latency adds up and one slow dependency takes down checkout. A no-hype guide to event-driven architecture: sync vs async, commands vs events, what brokers really promise (at-least-once, not exactly-once), choreography vs orchestration, and exactly when NOT to reach for events.
Splitting code is the easy half — splitting data is where distributed systems humble you. A practical guide to owning data across services: why a shared database is a distributed monolith, the trade from ACID to eventual consistency, the dual-write bug and the outbox that fixes it, sagas with compensating actions, and when CQRS and event sourcing are worth their lifetime cost.
The moment a call leaves your process it can be slow, fail, or happen twice — and that is the normal case, not the exception. The resilience toolkit, explained plainly: why timeouts come first, how retries become a self-inflicted DDoS without backoff and jitter, why idempotency is the price of retrying, and how circuit breakers, bulkheads, graceful degradation, and observability keep one bad dependency from taking down everything.
The database is almost always the first thing to buckle under growth — and the first thing engineers over-engineer in a panic. A no-hype ladder for scaling the data layer: why you measure and add an index before touching hardware, how read replicas exploit the read/write asymmetry (and the replication-lag trap they bring), where caching helps and why invalidation is the hard part, and when you finally reach for partitioning and sharding — the one decision that is genuinely hard to undo.
In a monolith, debugging was almost cosy — one log file, one process, one place the truth lived. Distributed systems quietly took that away: one request now fans out across a dozen services, and when it breaks there is no single log to read. A no-hype guide to seeing your system at scale: the three pillars (metrics, logs, traces) and the question each one answers, why a single propagated trace ID is the highest-leverage habit you can adopt, how SLOs turn reliability into an error budget you can spend, and how to alert on symptoms so on-call doesn't burn out.
Microservices can create real team autonomy, but they move cost into operations, data consistency, observability, testing, and coordination. This piece asks when that cost is worth paying and when a modular monolith is the kinder architecture.
A friendly, example-driven guide to Hexagonal Architecture: what ports and adapters really are, the one rule that makes it work, and exactly how much of it to apply — whether you are a solo founder or an enterprise team.
They look like three rival architectures. They are really the same idea — dependencies pointing inward, frameworks at the edge — drawn three ways. Here is what each one actually adds, and which to reach for.
DI containers feel like wizardry until you see the plain idea underneath: stop letting code create its own dependencies, and hand them in instead. A from-scratch guide, with real examples and the testing payoff.
Should your top-level folders be controllers/services/models, or orders/billing/auth? The choice quietly shapes how your codebase grows. A practical tour of by-layer, by-feature, and screaming architecture — with the trade-offs at each company size.
Microservices are a tax you pay for organisational scale — not a starting point. A no-hype walk from monolith to modular monolith to microservices, with the signals that tell you when (and whether) to split.
Splitting the frontend into independently deployable pieces can free teams — or bury you in complexity. Written from shipping an Angular-host + React-modules setup at scale: when micro-frontends pay off, and the bill they come with.
Layered architecture is easy to recognize; vertical slices keep change close to the feature people use. This piece compares both styles through everyday code changes and the boundary trade-offs behind them.
Dependency direction is the simple team rule that says stable business ideas should not depend on volatile delivery details. This explainer uses a small release scenario to show how the rule keeps code, reviews, and team conversations calmer.
A practical explanation of eventual consistency beyond the textbook definition: what users notice, which trade-offs teams are really making, how to set expectations in the product, and how operations keeps delayed truth from becoming silent data drift.
A practical explanation of CQRS: separating commands from queries, when read and write models deserve different shapes, and how to avoid turning a useful pattern into unnecessary architecture.
A practical look at serverless architecture: what teams gain by handing infrastructure to managed platforms, what they still own, and how to decide when functions, queues, and managed services are the right trade.
A practical explanation of caching strategies: where caches help, how cache-aside, read-through, write-through, TTLs, and invalidation differ, and why stale data, stampedes, keys, and observability matter as much as speed.
A practical explanation of database sharding: what changes when one database is split into smaller partitions, when sharding is worth the operational cost, and why shard keys, hotspots, rebalancing, and observability matter.
A practical comparison of GraphQL and REST: how each shapes API contracts, frontend flexibility, caching, observability, team ownership, and the operational trade-offs behind choosing one style over the other.
A practical explanation of idempotent API design: why retries happen, how duplicate requests create risk, and how idempotency keys, stable state transitions, and clear response contracts make distributed systems calmer.
A practical explanation of rate limiting and throttling: how systems protect shared capacity, shape traffic, communicate limits clearly, and keep overload from turning into a full outage.