Steven's Knowledge

Standards & Principles

The properties that make code good (the ends), the named principles that get you there (the means), and how the two trade off

Standards & Principles

Talking about "good code" means separating two things:

  • A standard is a property that makes code good — correct, readable, simple. These are the ends.
  • A principle is an actionable rule, repeatedly rediscovered, for reaching those properties — DRY, SOLID, YAGNI. These are the means.

The distinction matters: every principle is useful only because it serves some standard. Detached from a standard, a principle degrades into dogma — DRY for its own sake, abstraction for its own sake. When principles collide (they collide often), asking "which standard is this serving?" usually settles it.

This page is the map of the section: standards and principles gathered in one place, with each one's deep treatment linked.


Part 1: The standards (what "good" means)

Listed by priority — the earlier, the less negotiable. On conflict, the higher wins: correctness over cleverness, clarity over micro-optimization. Each comes with an actionable test.

1. Correctness

Meets the spec across all inputs — including boundaries, empties, concurrency, and the failure path — not just the happy path.

Test: have I thought about the boundaries (empty, single element, exactly at the limit), concurrency, and the error path?

Correctness is the foundation. No other property may be traded for it.

2. Readability

A reader understands the intent without a manual. Code is read far more often than it is written.

Test: how long would a colleague without context take to understand this and change it with confidence?

3. Simplicity

No unnecessary complexity; the complexity matches the problem rather than being added by how it was written.

Test: can I remove a parameter / branch / abstraction without losing functionality?

→ See Simplicity.

4. Maintainability / Changeability (ETC)

Cheap to change, local in impact, not scary to touch. A system's long-term cost is dominated by the cost of changing it.

Test: how many places does a common requirement change touch? Does it trigger a cascade?

→ See Design Principles · ETC.

5. Testability

Behavior can be verified in isolation. Hard-to-test is almost always a signal of tight coupling in the design, not merely "no tests yet."

Test: can I test this without standing up the whole system or talking to real dependencies?

→ See Testing.

6. Robustness

A well-defined behavior in the face of bad input and failing dependencies — not a crash, not a silent wrong answer.

Test: what happens when a dependency times out, throws, or returns garbage?

7. Consistency

Matches the surrounding code and the team's conventions, minimizing the reader's surprise.

Test: does this read as though the same person wrote it as the rest of the file?

Consistency has value in itself: one uniform "fine" convention usually beats a patchwork of locally-optimal ones.

8. Adequate performance

Meets the requirement under real load, and is measured rather than guessed. Performance is a constraint, not a default goal.

Test: is this bottleneck one a profiler found, or one I assumed?

→ See Performance.


Part 2: The principles (how to get there)

A principle names a force that earlier engineers found the hard way — ignore it and you get a predictable kind of pain. But none of them are laws; every one has counter-examples. They're grouped below by the standard they mainly serve.

A. Controlling complexity

  • KISS (Keep It Simple) — among solutions that do the job, pick the simplest.
  • YAGNI (You Aren't Gonna Need It) — don't write code for an imagined future requirement.
  • The four rules of simple design — passes the tests > reveals intent > no duplication > fewest elements; evaluate a design in that priority order. → Simplicity
  • Separation of Concerns — a piece of code attends to one concern at a time; distinct concerns live in distinct places.
  • Principle of Least Astonishment — an interface should behave the way its user expects; surprise is where defects breed.

B. Reducing coupling, removing duplication

  • DRY — every piece of knowledge has a single authoritative source. The point is knowledge, not code that looks alike. → Duplication
  • Orthogonality — changing one thing doesn't ripple into an unrelated thing; modules don't reach into each other's internals. → Duplication · Orthogonality
  • Single Responsibility (SRP) — a module is responsible to one kind of change, has one reason to change. → Design Principles
  • High cohesion, low coupling — related things together, unrelated things independent. → Design Principles · Coupling and Cohesion
  • Law of Demeter — talk only to your immediate friends; don't reach through a long a.b.c.d chain. → Design Principles · Law of Demeter
  • Dependency Inversion / program to an interface — high-level policy depends on an abstraction, not on a low-level mechanism's concrete type. → Design Principles · DIP
  • Composition over inheritance — assemble behavior by composition by default; reserve inheritance for a genuine "is-a." → Design Principles

C. Boundaries and contracts

  • Design by Contract — state preconditions, postconditions, and invariants explicitly; say what "correct" means. → Design by Contract
  • Fail Fast — surface an error as early and as close to its cause as possible; don't let a bad state spread far before it's noticed. → Error Handling
  • Validate at the boundary, trust the inside — clean untrusted data once at the entrance so the core logic doesn't have to guard everywhere. → Boundaries
  • Make illegal states unrepresentable — use types to rule out impossible states at compile time instead of checking for them at runtime.
  • Least privilege — each module gets only the minimum access and information it needs to do its job.

D. Abstraction and encapsulation

  • Encapsulation / information hiding — expose what, hide how; the implementation may change, the contract may not.
  • Single level of abstraction — within one function body, don't mix high-level intent with low-level detail; it should read at one altitude.
  • Open-Closed (OCP) — open to extension, closed to modification: add new behavior by adding code, not editing existing code. → Design Principles
  • Tell, Don't Ask — give the behavior to the owner of the data instead of pulling the data out to manipulate it elsewhere. → Design Principles

E. Expressing intent

  • Code as documentation — let the code state the intent; use comments for the why, not to restate the what. → Comments
  • Names that reveal intent — a name should survive review, refactor, and time; a good name is a comment the type system checks. → Naming
  • Program deliberately, not by coincidence — know why each line works rather than "it happened to run." → Pragmatic Mindset

F. Evolution and discipline


Part 3: Principles conflict — and that's where judgment lives

A beginner applies principles as a checklist, one by one; a senior knows principles pull against each other, and the skill is judging which one rules right now. The most common collisions:

ConflictHow to settle it
DRY vs decouplingRemoving duplication sometimes creates coupling. Two pieces of code that happen to look alike ≠ one piece of knowledge. Test: will they change for the same reason? If yes, merge; if they just resemble each other today, leave them apart.
Abstraction vs YAGNIPremature abstraction is speculative complexity. Wait for the third repetition (Rule of Three) before extracting; tolerate the first two.
Flexibility vs simplicityOpen an extension point only along the axis where change will actually come; keep the other dimensions closed and simple. Extensible everywhere is over-engineering.
Performance vs readabilityBe correct and readable first; once a profiler finds the bottleneck, trade readability for speed locally and with a comment. Don't sacrifice clarity for a bottleneck you never measured.
Consistency vs improvementIn a codebase with poor conventions, "consistent" and "better" collide. Improve in place at small scope; at large scope, unify the convention first — don't add a third style.

The tie-breaker: Correct > Clear > Simple > everything else. When two principles fight, go back to the standard each one serves and decide in that order.


Pre-commit checklist

  • Is this correct at the boundaries, on empties, under concurrency, on the failure path? (correctness)
  • Could someone without context read it and change it with confidence? (readability)
  • Is there anything I can delete without losing functionality? (simplicity)
  • How many places does a common change touch, and does it cascade? (changeability)
  • Can I test it without real dependencies? (testability)
  • Is the duplication I removed one piece of knowledge, or just code that looks alike today? (DRY vs decoupling)
  • Is the abstraction / extension point I added for a change that has happened, or for an imagined future? (YAGNI)
  • Which principles conflict here — and did I settle it by "Correct > Clear > Simple"?

To see these standards and principles applied to concrete snippets, see Bad Code, Good Code.

On this page