Chapter 2: A Pragmatic Approach

Table of Contents


Topic 8: The Essence of Good Design

Author's Suggestion

Something is well-designed if it adapts to the user. For code, this means that it's adaptable to change. This is where the ETC principle -- Easier To Change -- comes into play.

Every Design Principle can be seen as a specific application of the ETC Principle.

My Application

I aim to apply the ETC Principle whenever I write code, always making it adaptable to change.

ETC Is a Value, Not a Rule

Author's Suggestion

A value is something that helps you make decisions. When writing code, the ETC Principle serves as a guide, helping you choose between different paths. Like any other value, it should live in your subconscious -- but to get there, you may need some conscious reinforcement.

Take time to reflect on whether the changes you've made actually make the code easier to change -- even something as small as saving a file can be a good moment for that reflection.

ETC has an implicit premise: it assumes the person knows which path will allow smoother future changes. Common sense is often enough, but not always. When it isn't, consider these two approaches:

  1. Modify the code so it can be easily replaced in the future.
  2. Write down the situation -- the options for change you see, and your assumptions about how adaptable the code would be for each. Keep a reference in the source code so you can revisit and verify your reasoning later.
My Application

In my day-to-day work as a coder, I aim to always ask myself: Does this change make the code more adaptable to future changes?

I rely on common sense by default, and when the answer isn't clear, I either modify the code so it can be easily replaced or make my best guess, later tracking whether I was accurate.

Reflections

  • Yes, Open for Extension, Closed for Modification Principle allows for easier changes because it enables adding new features without breaking existing code.
  • Let's analyze how well the following paradigms work with ETC:
  1. OOP
  • Positives for ETC: Encapsulation and polymorphism isolate changes.
  • Negatives for ETC: Deep inheritance and over-abstraction create rigidity.
  • Summary: Flexible if composition is favored over inheritance.
  1. Functional
  • Positives for ETC: Pure, composable, immutable code is safe to refactor.
  • Negatives for ETC: Harder to manage side effects and shared state.
  • Summary: Excellent for logic; awkward for stateful systems.
  1. Event-Driven
  • Positives for ETC: Decoupled via events; easy to add handlers.
  • Negatives for ETC: Complex callback chains; hard to trace flows.
  • Summary: Good for dynamic systems if structured.
  1. Reactive
  • Positives for ETC: Data flows automatically propagate changes.
  • Negatives for ETC: Debugging asynchronous data streams can be tricky.
  • Summary: Excellent for live data and continuous updates.
  1. Procedural
  • Positives for ETC: Simple and direct for small programs.
  • Negatives for ETC: Global mutable state is fragile for large systems.
  • Summary: Low ETC in large systems.
  1. Component/Modular
  • Positives for ETC: Clear boundaries enable replacement and reuse.

  • Negatives for ETC: Integration and versioning overhead.

  • Summary: High ETC if interfaces remain stable.

  • When coding, I can eliminate the negatives by avoiding actions that make code harder to change. To accentuate the positives, I aim for situations that support the ETC principle.

  • Remember: use your editor to provide reminders whenever you save your code to check if it remains easy to change.

Topic 9: DRY - The Evils of Duplication

DRY Is More Than Code

Author's Suggestion

Programmers work with knowledge through software development lifecycle (SDLC), and document it in specifications that come alive in code or during testing.

Maintenance is not a reaction to bugs; it is a routine part of the SDLC. When maintaining software, we often need to update the way knowledge is represented. This can easily introduce duplication in specifications, processes, or code, making maintenance more difficult -- even before the application is live.

The key to developing reliable software and making it easier to understand and maintain is the DRY principle. DRY is not just about avoiding repeated code; it is about avoiding repeated knowledge and intent. For example, if a single change requires modifying many parts of the code, that signals a DRY violation.

Every knowledge must be a single source of truth within a system.

My Application

I used to think DRY applied only to repeated code, but I now understand it also applies to repeated knowledge.

Duplication in Code

Author's suggestion

Whenever possible, identify actions repeated across your code that serve the same intention and consolidate them.

Not all Code Duplication Is Knowledge Duplication

The functions with identical code do not necessarily violate DRY if their intentions differ.

My Application

I used to create functions automatically when I saw duplicate code. Now, I first check whether the code truly duplicates knowledge or if it has a different purpose.

Duplication in Documentation

Author's suggestion

Let the code explain itself. Avoid overusing comments.

Dry Violations in Data

Identify if a field can be calculated from other fields. If so, make it a calculated field; storing it separately would violate DRY.

Sometimes, it is acceptable to violate DRY intentionally, such as when caching data for performance.

My Application

I strive to write self-explanatory code so that comments are minimal and meaningful.

Representation Duplication

Author's suggestion

Code often interacts with external systems, so some duplication is natural when working with APIs, external services, or external data sources.

Mitigation strategies:

  • Duplication Across Internal APIs: Use tools to define APIs in a neutral format.

  • Duplication Across External APIs: Import the API specification into your local tools (e.g., via OpenAPI) to integrate reliably with the service.

  • Duplication with Data Sources: Let persistence frameworks generate containers directly from the schema or use a dictionary-like structures with table-driven validation to ensure the data matches the required format leveraging your API documentation tool.

My Application

I embrace a single-source-of-truth mindset when working with interfaces, both internally and externally. For data sources, I avoid redundant DTOs and rely on dictionaries with table-driven validation, or schema-generated containers to stay DRY.

Interdeveloper Duplication

Author's suggestion

When multiple developers work on a project, duplication in the code base is almost guaranteed. One way to mitigate this is to designate a project librarian who:

  • Manages common scripts in the source tree.
  • Reviews documentation and source code for duplication.
  • Supports other developers in following the same approach. The goal is to make reuse easy and avoid unnecessary duplication.
My Application

I proactively define common functionalities within the team and maintain documentation that is unique, clear, and easy to use.

Topic 10: Orthogonality

Topic 11: Reversibility

Topic 12: Tracer Bullets

Topic 13: Prototypes and Post-it Notes

Topic 14: Domain Languages

Topic 15: Estimating