Design Principles: KISS, SOLID, CAP, BASE
The short list of acronyms that quietly shape almost every system-design decision.
Why principles, not just patterns
Patterns tell you what to build; principles tell you how to decide — which pattern, how much of it, and when to stop. This day collects the handful of acronyms that recur in nearly every design discussion. They are compact because they are meant to be recalled under pressure, in a review or an interview, as a quick check on a decision you are about to make.
Treat each as a lens, not a law. KISS and SOLID guide how you structure code and services; CAP and BASE guide how you reason about data under failure. Knowing them by name lets you say why a design is shaped the way it is, which is most of what a strong design defense is.
KISS — Keep It Simple
KISS — "Keep It Simple, Stupid" — says to prefer the simplest design that solves the actual problem, and to add complexity only when a real, present need forces it. Simplicity here means fewer moving parts, fewer abstractions, and fewer things that can break or confuse the next reader.
The trap KISS guards against is solving problems you do not have yet. Reaching for sharding, event streams, and multiple regions before any of them is justified adds cost and failure surface for no benefit. Its close cousin is YAGNI — "You Aren't Gonna Need It" — which says do not build a capability until it is actually required, because speculative flexibility is usually wrong flexibility. Together they push you toward the smallest thing that works, evolving it only where the system genuinely strains.
- What it prevents. Over-engineering — complexity, and the bugs and maintenance cost that ride along with it, added ahead of any real need.
- In practice. Start with the baseline architecture; add each component only in response to a bottleneck you can name.
SOLID — five principles for structuring code
SOLID is five object-oriented design principles that, together, keep code changeable: easy to extend, hard to break by accident. Each addresses one way that code becomes rigid or fragile as it grows. They apply to classes most directly, but the same instincts scale up to modules and services.
| Letter | Principle | One-line meaning |
|---|---|---|
| S | Single Responsibility | A unit should have one reason to change |
| O | Open-Closed | Open to extension, closed to modification |
| L | Liskov Substitution | A subtype must be usable anywhere its base type is |
| I | Interface Segregation | Prefer small, specific interfaces over one fat one |
| D | Dependency Inversion | Depend on abstractions, not concrete implementations |
Single Responsibility. A class or module should do one job, so there is only one kind of reason to change it. A Report class that both computes numbers and formats PDFs has two reasons to change — split it, so a formatting tweak cannot break a calculation.
Open-Closed. You should be able to add new behavior by adding new code, not by editing code that already works. A payment processor that handles new payment types by accepting a new PaymentMethod implementation — rather than by adding another branch to a growing if/else — is open for extension, closed for modification.
Liskov Substitution. Anywhere the base type is expected, any subtype must work without surprising the caller. If Square subclasses Rectangle but breaks the assumption that width and height set independently, code that works on Rectangle breaks on Square — a Liskov violation.
Interface Segregation. Do not force a class to depend on methods it does not use. Instead of one giant Machine interface with print, scan, and fax, split it so a simple printer implements only Printer and is not dragged into faxing.
Dependency Inversion. High-level logic should depend on an abstraction, and the concrete detail should plug into that abstraction. An OrderService that depends on a PaymentGateway interface — not directly on StripeClient — can swap implementations and be tested with a fake.
- What it prevents. Rigid, tangled code where one change ripples unpredictably and every extension means editing working code.
- In practice. These are guidelines, not quotas — apply them where code is changing and painful, not mechanically everywhere (KISS still rules).
CAP — the partition-time choice (recap)
CAP states that when a network partition splits your replicas so they cannot communicate, you must choose between consistency (every read sees the latest write) and availability (every request still gets an answer) — you cannot have both while partitioned. That is the one-line recap; the CAP theorem is this course's earlier capstone, so here the focus is on how it drives choices rather than re-deriving it.
The way CAP shapes a design is per-operation, not per-system. You ask, for each piece of data: during a partition, is a stale-or-conflicting answer dangerous, or is no answer worse? That single question sorts your operations into two piles.
| Data / operation | Danger of a stale answer | CAP lean |
|---|---|---|
| Account balance, inventory count | Money loss or overselling | CP — protect consistency |
| Permissions, locks, identity | Security or correctness breach | CP |
| Like count, feed, presence | Harmless, self-corrects | AP — stay available |
| Shopping cart, telemetry | Mergeable or replaceable | AP |
The design move is to route each workflow to CP or AP behavior by the cost of being wrong — CP where a wrong answer is worse than no answer, AP where no answer is worse than a slightly stale one.
BASE — the NoSQL counterpoint to ACID
BASE is the consistency model many NoSQL and large-scale systems adopt as a deliberate alternative to ACID. It stands for Basically Available (the system answers even under partial failure), Soft state (data may be in flux, not immediately settled, as replicas sync), and Eventual consistency (given no new writes, all replicas converge to the same value in time). It is what an AP system looks like as a data philosophy: favor staying up and converging later over being strictly correct at every instant.
BASE is best understood against ACID, the traditional transaction guarantee — Atomic (all-or-nothing), Consistent (never violates rules), Isolated (concurrent transactions don't interfere), Durable (committed data survives). ACID prizes correctness at the moment of the transaction; BASE prizes availability and scale, accepting a window of inconsistency.
Neither end is "better" — they sit on a spectrum, and you place each dataset where its cost of inconsistency lands:
| Model | Guarantee | Cost | Typical home |
|---|---|---|---|
| ACID | Strong, immediate consistency | Coordination, locking, harder to scale writes | Ledgers, orders, identity |
| BASE | Eventual consistency, high availability | Temporary staleness, merge logic needed | Feeds, carts, counters, telemetry |
The practical read: use ACID where a moment of wrongness is unacceptable, BASE where availability and scale matter more than instant agreement — often both, in different parts of the same product.
ACID <--------------------------------------> BASE strong eventual consistency consistency correctness-first availability-first SQL / financial ledgers NoSQL / feeds, carts, logs locks, transactions async replication, merge
PACELC — refining CAP with latency
PACELC extends CAP to cover the normal case, not just the failure case. It reads: if there is a Partition, choose between Availability and Consistency (that's CAP); Else — when the system is running fine — choose between Latency and Consistency. The insight PACELC adds is that even with no partition, strong consistency has a price: coordinating replicas to agree takes time, so you are trading latency for consistency every day, not only during failures.
- What it adds over CAP. CAP only speaks to partition time; PACELC names the everyday trade — lower latency versus stronger consistency — that you pay on every request.
- In practice. A system described as "PA/EL" (available under partition, low-latency otherwise) is tuned for speed and availability throughout; "PC/EC" is tuned for consistency in both regimes. It gives you a two-part label instead of a single CAP point.
Mapping each principle to the problem it prevents
The fastest way to internalize these is to hold each against the failure it exists to stop. Read the table as "reach for this principle when you feel that problem coming."
| Principle | Problem it prevents | Guiding question |
|---|---|---|
| KISS / YAGNI | Over-engineering and speculative complexity | Is this complexity solving a problem I have now? |
| Single Responsibility | One change breaking unrelated behavior | Does this unit have more than one reason to change? |
| Open-Closed | Editing working code to add features | Can I extend this without modifying it? |
| Liskov Substitution | Subtypes that break their callers | Can I use any subtype without surprises? |
| Interface Segregation | Fat interfaces forcing unused dependencies | Am I forced to depend on methods I don't use? |
| Dependency Inversion | Logic welded to a concrete implementation | Do I depend on an abstraction or a detail? |
| CAP | Ignoring partition behavior until it bites | During a partition, do I need CP or AP here? |
| BASE / ACID | Wrong consistency model for the data | Is a moment of staleness acceptable for this data? |
| PACELC | Forgetting consistency costs latency every day | Am I paying latency for consistency I don't need? |
The unifying habit: name the principle, name the problem it prevents, and you have both a decision and its justification — which is exactly the "say why, not just what" discipline this whole course is built on.
Key takeaways
- KISS and YAGNI push for the simplest thing that works, adding complexity only for a present, named need — the antidote to over-engineering.
- SOLID's five principles keep code changeable: one reason to change, extend without modifying, honor subtypes, keep interfaces small, and depend on abstractions.
- CAP is a per-operation choice under partition — route each workflow to CP (consistency-first) or AP (availability-first) by the cost of a wrong answer.
- BASE (basically available, soft state, eventual consistency) is the availability-first counterpoint to ACID; place each dataset on the ACID↔BASE spectrum by its tolerance for staleness.
- PACELC refines CAP: even with no partition, you trade latency against consistency on every request.
- Each principle pairs with a specific problem it prevents — naming both gives you a decision and its defense.
Checklist
- [ ] Can you explain KISS and YAGNI and give an example of over-engineering they prevent?
- [ ] Can you state each SOLID letter with a one-line meaning and a tiny example?
- [ ] Can you explain how CAP drives a per-operation CP-versus-AP choice rather than a whole-system label?
- [ ] Can you define ACID and BASE and place a dataset correctly on the spectrum between them?
- [ ] Can you state what PACELC adds over CAP, in the non-partition (else) case?
- [ ] Can you map each principle to the specific problem it prevents?