Skip to content

Instantly share code, notes, and snippets.

@chaotic3quilibrium
Last active April 23, 2026 15:11
Show Gist options
  • Select an option

  • Save chaotic3quilibrium/52f0b07f6c4224b2f2ffe6dcd47a2ad9 to your computer and use it in GitHub Desktop.

Select an option

Save chaotic3quilibrium/52f0b07f6c4224b2f2ffe6dcd47a2ad9 to your computer and use it in GitHub Desktop.
The tension between the SRP (Single Responsibility Principle) and DRY (Don't Repeat Yourself Principle)

Given the two software engineering principles, A and B, below the "---"...

  1. What kinds of contexts arise where they are in conflict with wach other?
  2. Wouldn't they more accurately be call a bias or guideline, than a principle?

A. The Single Responsibility Principle (SRP) states that a class or module should have one, and only one, reason to change, meaning it should focus on a single task or actor. As the 'S' in SOLID, it separates concerns to ensure that changes in one business rule do not necessitate changes in others, making code easier to maintain, test, and understand. B. The DRY (Don't Repeat Yourself) principle is a software development guideline stating that "every piece of knowledge must have a single, unambiguous, authoritative representation within a system". It aims to reduce duplication of logic, data, and documentation, ensuring that changes are made in one place to improve maintainability and reduce bugs.

The tension between the Single Responsibility Principle (SRP) and Don't Repeat Yourself (DRY) is a classic architect’s dilemma. While they often work in tandem to improve maintainability, they reach a breaking point when the "reason to change" for one module diverges from the "knowledge" represented in another.


1. Contexts of Conflict

The primary conflict arises from Accidental Duplication versus Essential Duplication.

Semantic vs. Syntactic Duplication

DRY is often misinterpreted as "never have the same lines of code twice" (syntactic). However, SRP cares about who requested the code.

  • The Scenario: Imagine a Customer object in a sales system and a Recipient object in a shipping system. Both require a validateAddress() method. Currently, the logic is identical.
  • The DRY approach: Move validateAddress() to a shared utility or base class to avoid repetition.
  • The SRP conflict: If the Shipping department changes their rules (e.g., they no longer deliver to P.O. boxes) but the Sales department still allows them for billing, the shared code breaks. By following DRY, you have coupled two different "reasons to change."
  • The Resolution: SRP suggests that even if the code looks identical today, they represent different pieces of knowledge. Keeping them separate (duplication) is safer than coupling unrelated business actors.

Microservices and Shared Libraries

In distributed systems, DRY often suggests creating a "Common" library for DTOs (Data Transfer Objects) used by multiple services.

  • The Conflict: If Service A needs to update a shared DTO for a new feature, it might force a deployment or cause a breaking change for Service B, C, and D.
  • The Trade-off: SRP (at a service level) suggests each service should own its domain models. Breaking DRY by duplicating the DTO across services provides decoupling, allowing services to evolve independently.

Boilerplate vs. Abstraction

Refactoring for DRY often introduces Abstractions (generics, inheritance, or higher-order functions).

  • The Conflict: If an abstraction is created to merge three slightly similar methods, the resulting "Generic Manager" often ends up with dozens of if/else or switch statements to handle the edge cases of each original caller.
  • The Result: You have satisfied DRY, but you have violated SRP because the new class now has multiple reasons to change (every time any of the three original callers changes their requirements).

2. Principle, Guideline, or Bias?

You are hitting on a nuanced point of software philosophy. The labels we use—Principle, Guideline, Heuristic, or Bias—reflect how much "authority" we give these ideas.

Term Perspective
Principle Suggests a fundamental law. In software, these are rarely "laws" because they can't be mathematically proven to be "correct" in all contexts. They are more like the "First Principles" of a specific school of thought (like Object-Oriented Programming).
Guideline A softer, more practical term. It suggests, "This is generally a good idea, but use your judgment." This is how most senior engineers treat SRP and DRY.
Heuristic Perhaps the most accurate engineering term. It’s a "rule of thumb" that helps solve problems faster. You don't need to reinvent the wheel every time; you apply the SRP heuristic until it stops working.
Bias This is an insightful way to look at it. SRP represents a Bias toward Decoupling. DRY represents a Bias toward Consistency.

Calling them "Principles" is often a pedagogical choice—it makes them easier to teach and provides a common vocabulary for code reviews. However, in the trenches of legacy refactoring or system design, they are effectively competing biases.

The "Senior Engineer" move is recognizing when the cost of the Abstraction (required by DRY) exceeds the cost of the Redundancy (allowed by SRP).

How do you typically decide when a "syntactic" similarity has become "semantic" enough to warrant a shared abstraction?

Great answer. The answer seems to bias towards favoring SRP over DRY. What contexts, if any, might exist where DRY would be preferable over SRP?

It is true that modern architectural trends (Microservices, Hexagonal Architecture) tend to lean toward SRP and decoupling, which can make DRY feel like the "junior" principle. However, DRY is not just about avoiding typing; it is about Knowledge Management.

When the cost of a "drift in logic" is higher than the cost of "tight coupling," DRY becomes the dominant priority.


Contexts Where DRY is Preferable

1. The Single Source of Truth (SSoT) for Business Rules

In domains where inconsistency is a critical failure (e.g., Finance, Medical, or Legal), DRY is non-negotiable.

  • The Scenario: A formula for calculating compound interest or a specific tax regulation.
  • The Case for DRY: If you duplicate this logic to satisfy SRP (allowing Sales and Accounting to evolve independently), you risk a scenario where one department calculates a value differently than another. In an audit, "we decoupled our modules" is not a valid defense for a multi-million dollar discrepancy.
  • The Strategy: Centralize the "Truth" in a single, authoritative service or library.

2. Security, Cryptography, and Compliance

Security logic is one of the most dangerous places to have "accidental duplication."

  • The Scenario: Logic for JWT validation, password hashing, or PII (Personally Identifiable Information) masking.
  • The Case for DRY: If a vulnerability is found in how you handle token expiration, you want to fix it in exactly one place and have it propagate everywhere. If you have "S-R-Ped" your auth logic into five different service-specific implementations, you have five different surfaces to audit and five places to potentially miss a patch.

3. Cross-Cutting Infrastructure Concerns

Standardizing how a system behaves "out of the box" requires strict adherence to DRY.

  • The Scenario: Logging formats, telemetry headers, and standardized error responses.
  • The Case for DRY: If every module defines its own "Responsibility" for how it logs data, the SRE (Site Reliability Engineering) team will find it impossible to build unified dashboards. By forcing a DRY implementation of infrastructure through a shared library or sidecar, you ensure the system is observable as a cohesive whole.

4. API Contracts and Data Protocols

When two systems or modules communicate, they must share a "Single, Unambiguous Representation" of the data being exchanged.

  • The Scenario: A Protobuf definition, a JSON Schema, or a database migration script.
  • The Case for DRY: If the producer and consumer have separate, duplicated definitions of the data structure (SRP-focused independence), the system will eventually fail when one side updates its schema and the other doesn't.
  • The Strategy: The schema itself is the "single piece of knowledge" that must be shared.

DRY as a Defense Against "Shotgun Surgery"

A major context for favoring DRY is when you are dealing with Shotgun Surgery—a code smell where every time you make a single business change, you have to touch a dozen different classes.

If you find yourself changing the same logic in five places, you haven't achieved SRP independence; you've actually created a "hidden coupling" where the classes are physically separate but logically tethered. In this case, refactoring toward DRY (centralizing that knowledge) actually makes the system more maintainable because it makes that implicit coupling explicit.

The "Cost" Comparison

Aspect Favoring SRP (Duplication) Favoring DRY (Abstraction)
Primary Risk Logic Drift: Different parts of the system start disagreeing on facts. Fragile Abstractions: A change for one user breaks the logic for everyone.
Complexity High "mental surface area" (more code to read). High "cognitive load" (deeper call stacks/indirection).
Best For Fast-moving, independent features. Critical, high-stakes core logic.

When you look at your own work—especially when designing strict data languages or refactoring legacy systems—do you find that "Logic Drift" or "Fragile Abstractions" tends to be the more common source of production bugs?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment