Code Design Guidelines

Code Design Guidelines

Hey there! If you're like me, always hunting for ways to make your code cleaner and your life as a developer easier, you've come to the right place. I've spent a good chunk of my time diving into some awesome books like "Clean Code", "Code That Fits In Your Head", "Domain Modeling Made Functional", and many more. Not to mention, I've been around the block a few times myself, coding and figuring things out through trial and error.

So, what's this all about? I've put together some of the coolest tips and tricks I've learned along the way. These aren't just any tips; they're game-changers that can make your code more straightforward, safer, and just plain better.

  • Make Illegal State Unrepresentable
    If the state is invalid, it's best to design API so that it's impossible to express it in code. Capture the absence of a capability in the API's design, so that something that should be impossible doesn't even compile. A compiler error gives you faster feedback than a runtime exception.

  • Minimize Mutable State
    Favor immutability to reduce complexity and unexpected behavior from state changes. Immutable objects are easier to reason about, debug, and test since their state cannot change after creation, reducing side effects and making the flow of data through your application more predictable.

  • Prefer Pure Functions
    Aim for functions that have no side effects and produce the same output for the same input. Pure functions are easier to test, debug, and parallelize, contributing to the overall reliability and maintainability of your code.

  • Use Meaningful Error Handling
    Instead of generic exceptions, create specific error types that convey meaningful information about what went wrong. This approach helps with debugging and allows callers to handle different error cases appropriately.

  • Principle of Least Astonishment
    Design your API so that it behaves in a way users would naturally expect without surprises. This reduces the learning curve and the likelihood of misuse.

  • Design for Testability
    Write your code with testing in mind. This means designing for injectable dependencies, minimal side effects, and clear separations of concerns, making it easier to write tests that are comprehensive and maintainable.

  • Optimize for deletability

    Aim for creating software components that are so well-isolated and understood that, if necessary, they can be completely recreated within a few hours or days. This not only improves the overall code quality and flexibility but also encourages a culture of continuous improvement and simplification within development teams.

  • Fail Fast
    Validate inputs or state as early as possible to catch errors early in the execution flow. Early failure makes it easier to debug and ensures that errors are caught before they can cause more damage.

  • Explicit over Implicit
    Be explicit about behaviors and dependencies rather than relying on implicit behavior or magic. This makes the code easier to understand, debug, and use correctly.

  • Command Query Separation
    Methods with side effects should return no data (void). That makes it trivial to recognize them. Such methods are called Commands. Methods that do return data should have no side effects. Such methods are called Queries.

  • Don't say anything with a comment you can say with a method name. Don't say anything with a method name you can say with a type
    In priority, from most important to least important:

    • Guide the reader by giving APIs distinct types

    • Guide the reader by giving methods helpful names

    • Guide the reader by writing good comments

    • Guide the reader by providing illustrative examples as automated tests

    • Guide the reader by writing helpful commit messages in Git

    • Guide the reader by writing good documentation