Skip to main contentSkip to user menuSkip to navigation

Design Principles & Laws

Essential design principles and laws: SOLID, DRY, KISS, YAGNI, Conway's Law, and more.

20 min readIntermediate
Not Started
Loading...

What are design principles?

Design principles are reusable decision rules that protect an important system property while accepting a known cost. For example, loose coupling protects independent change and fault isolation, but introduces contracts, versioning, and sometimes asynchronous delivery. They are not rules to apply everywhere.

Start with a concrete pressure: a change that repeatedly breaks unrelated code, a dependency failure that blocks users, traffic that exceeds one machine, access that is too broad, or data that cannot be trusted. Then choose the smallest boundary and mechanism that addresses that pressure.

The invariant is: every principle needs a named benefit, boundary, cost, and observable test. Without all four, a principle is a slogan rather than an architectural decision.

Select from the constraint, not a favorite principle

The same system can need several principles, but they should solve distinct problems. Start with the pressure that is currently expensive or risky, then make the resulting trade-off explicit.

  1. 1

    Observed problem

    Name the pressure

    Describe the failed deployment, slow request, security exposure, or incorrect record. Avoid starting with a pattern name.

  2. 2

    Where it belongs

    Choose a boundary

    Decide whether the protection belongs inside a module, between services, at a data store, or at an access boundary.

  3. 3

    What must remain true

    State the invariant

    Write a testable claim such as "a recommendation outage cannot block checkout" or "a worker can only read its own queue".

  4. 4

    Evidence

    Measure the cost

    Track the added latency, operational work, coordination, or cognitive load and verify that it buys the intended protection.

Constraint-driven selector

Choose the property that needs protection

Change the observed pressure, the boundary, and the complexity posture. The recommendation names a principle, an invariant, and the cost that must remain visible.

1. Observed pressure
2. Smallest boundary
3. Complexity posture
Recommended principle

Loose coupling with graceful degradation

Chosen from the pressure, not a pattern catalog.

Boundary action

Service

Set a timeout, use a versioned contract, and define a bounded fallback.

Testable invariant

An optional dependency outage cannot block the critical user task.

Evidence: Measure timeout rate and successful degraded completions.

Cost to accept

Fallbacks can be stale and need explicit ownership.

Decision posture

Add the smallest mechanism that can be measured against the stated invariant.

Make the principle testable

A principle becomes useful only when it changes a design decision. Write its benefit as an invariant, then identify the evidence that could disprove it.

1 owner

Responsibility

One reason for a module to change

2 paths

Degraded mode

Primary path plus a bounded fallback

N replicas

Scale-out

Capacity grows by adding equivalent workers

0 excess grants

Privilege

Permissions match the caller's actual job

A lightweight estimate before adding machinery

Estimate the pressure before proposing an abstraction. If a service receives 4,000 requests per second and one instance handles 800 requests per second at the latency target, the starting capacity is 4,000 / 800 = 5 instances. Add headroom for failures and uneven load before claiming horizontal scaling is complete.

For reliability, turn an availability target into downtime: 99.9% monthly availability permits about 43 minutes of downtime in a 30-day month; 99.99% permits about 4.3 minutes. The tighter target may justify redundancy and graceful degradation, but it also increases coordination and operational cost.

Use estimates to decide whether the pressure is real enough to warrant complexity. A rarely changed internal script does not automatically need a plugin architecture; a frequently changed payment rule might.

Place principles at architecture boundaries

The boundary tells you where a principle applies. A well-named boundary makes both ownership and failure behavior easier to inspect.

Checkout request with an optional recommendation dependency

Checkout owns the transaction. Recommendations enrich the response but cannot control the purchase path.

Request

Client

Critical owner

Checkout API

Consistency boundary

Order store

Optional dependency

Recommendations

  • Single responsibility: Checkout owns order validation and persistence; recommendations own suggestions. A request handler that owns both will have unrelated reasons to change.
  • Loose coupling: Checkout reads a versioned recommendation contract or continues without it. It does not reach into the recommendation database.
  • Fail fast and data consistency: Validate the order at the boundary and commit the authoritative order record before emitting downstream work.
  • Graceful degradation: A timeout from recommendations produces an order confirmation without suggestions, not a failed purchase.

Refactor an architecture smell without moving the failure

Refactoring is successful when it improves the named invariant and preserves a safe behavior during the migration. Select a smell, choose a response, and see what failure still reaches the user.

Architecture smell lab

Refactor the failure path, not only the code shape

Select a smell and proposed refactor. A useful change needs the right principle plus migration and operational guards that preserve behavior while it rolls out.

1. Architecture smell
2. Proposed refactor
3. Migration guards
Principle under test

Graceful degradation and bounded dependency failure

The protection this refactor must actually provide.

Migration guards

2/2

Behavior and production signals are covered.

Current failure behavior

A non-critical outage becomes a customer-visible payment failure.

Ready to stage

The dependency can fail visibly without controlling the critical task.

Safe rollout: Set a timeout, serve a defined fallback, emit telemetry, and keep the authoritative order path independent.

Use the reference at the decision boundary

These principles commonly appear together, but each should have its own reason and measurement. Open an item only after you can name the pressure it addresses.

A module remains understandable because one cohesive responsibility owns one kind of change. Extension points let new variations arrive without repeatedly editing stable behavior.

  • Use it when: Pricing rules, notification channels, import formats, or policies change independently of the request path that invokes them.
  • Test the invariant: Adding a new rule changes the rule implementation and registration, not unrelated checkout, storage, and presentation code.
  • Cost and limit: Interfaces and indirection can hide a simple flow. Do not create a plugin system for a variation that is unlikely to exist.
  • Smell: A class described with "and," or a growing switch statement that mixes unrelated policy and orchestration.

Operate the principle, not just the diagram

Architecture decisions decay when their assumptions are not monitored. Attach an owner, signal, and response to each protected property.

  • Change boundaries: Track deployment lead time, change failure rate, and the number of callers coupled to an internal implementation.
  • Dependency isolation: Measure timeout rate, fallback activation, retry volume, and the percentage of critical requests that complete in degraded mode.
  • Capacity: Monitor utilization, queue depth, p95 latency, instance count, and headroom after losing one failure domain.
  • Security: Audit grants, token age, denied actions, anomalous access, and the time needed to revoke a compromised credential.
  • Data: Record validation failures, freshness age, reconciliation lag, duplicate events, and correction time.

Avoid principle theater: an interface with one implementation, a circuit breaker with no fallback behavior, or roles that all grant administrator access do not provide the protection their names imply.

Continue learning

No quiz questions available
Could not load questions file
Spotted an issue or have a better explanation? This page is open source.Edit on GitHub·Suggest an improvement