1. The Common Module Trap: Organize Code Around Concrete Capabilities

    Delay common modules until ownership is clear, and organize logging, exceptions, and shared protocols around concrete capabilities instead.

  2. How to Break Up a Giant Service Class Without Guesswork

    Split an oversized service by mapping concepts, tracing responsibilities, testing boundaries, and comparing concrete refactoring options.

  3. Create a Deliberate Messy Boundary to Keep the Core Clean

    Contain unavoidable complexity in an explicit outer boundary so limited engineering time can protect the system's core concepts.

  4. Layering That Teammates Can Understand—and the Build Can Enforce

    Define stable layer roles, constrain optional upper layers, and automate checks only when a team needs stronger enforcement.

  5. Name Readers, Finders, and Searchers by Behavior

    Distinguish Reader, Finder, and Searcher classes by direct reads, added filtering, and composite searches rather than result count.

  6. Earn Your Interfaces: Abstraction After Evidence

    Start with concrete code, extract interfaces from proven variation, and keep one-to-one abstractions only when they create a real boundary.

  7. Authorization Without Redundant Reads

    Resolve the logged-in user once, keep access checks focused, and separate system-wide administration from channel-level permissions.

  8. Don't Distort Production Code Just to Make Tests Easier

    Change production code when its behavior becomes clearer or legacy code needs a testing seam—not merely to expose values or methods that only tests use.

  9. Your First Test Can Be Messy—Refactor It Later

    Start with the test that proves the behavior you care about, mock enough to make it run, and improve test design through repeated use and review.

  10. Design Performance Tests from Real Traffic Shapes

    Estimate volume and arrival patterns before choosing a load test, add deliberate headroom, and require evidence only where traffic risk justifies it.

  11. Reliable Database Tests Without a Shared Test Database

    Choose mocks, an in-memory database, Testcontainers, or a real database by the failure you need to catch, while keeping ordinary tests isolated from shared infrastructure.

  12. Software Should Survive the Developer Who Built It

    Good company software reduces debt, fits the team's operating ability, and remains understandable and repairable after its original developer leaves.

  13. Unit Tests Should Preserve Business Intent

    A business-layer unit test is useful when it guides design, records meaningful behavior, or makes the next developer reconsider a risky change.

  14. Use Reader and Writer Components to Reveal Business Flow

    Reader and writer components can hide storage details, narrow change, and let the business layer show policy, but only when the software's lifetime justifies them.

  15. How to Split a Large Service Class by Responsibility and Layer

    Use constructor dependencies and imports to diagnose an oversized service, then separate cohesive responsibilities before adding another architecture label.