Learning outcome
You should be able to explain DRY as a design principle about duplicated knowledge, not merely duplicated text, and apply it in C# without turning every similarity into a shared base class, utility, or helper. In interviews, the best answer usually starts with: “I want one source of truth for a business rule, but I do not want to collapse different responsibilities into a single abstraction just because two code paths look alike.”
Intuition
DRY is often misunderstood as “never repeat any code.” That interpretation is too literal. Two code fragments can look similar and still deserve to stay separate because they change for different reasons. The real problem is when the same business rule is encoded in multiple places and can drift out of sync.
A useful C# example is a pricing rule used by checkout and invoices. Suppose both paths must apply the same discount threshold, tax category, or rounding policy. That rule should live in one place, because it is the same business knowledge. If checkout calculates “10% off orders over 100” and invoices independently re-implement the same rule, a future change to 120 or 15% becomes a bug magnet.
But now imagine checkout also needs to reserve inventory, and invoices also need to format legal text. Those responsibilities are not the same business knowledge. Extracting all of that into one shared abstraction would create coupling between unrelated workflows. The result is often worse than duplication: a small change in one area forces edits in a shared component that was never truly a common domain concept.
Deep dive
A good DRY refactor starts by asking: What is the duplicated knowledge? If the answer is “the discount threshold and rate,” then share that rule explicitly. If the answer is “two methods both use if and return,” that is only structural similarity, not a reason to merge them.
For a pricing-rule example, imagine checkout and invoices both need to determine whether an order qualifies for a loyalty discount. That rule may be expressed as a dedicated domain service, a value object, or a policy function. The key is that the abstraction represents a real concept in the business domain, not just a convenience for code reuse.
A healthy extraction usually has these traits:
- It names a business concept, such as
DiscountPolicyorPricingRules. - It contains logic that truly belongs together because it changes together.
- It is depended on by multiple callers that need the same answer.
- It does not pull unrelated workflow steps along with it.
A common failure is overgeneralization. For example, you might notice that both checkout and invoices need order totals. One calculates totals before payment, the other prints totals after fulfillment. Those totals may share arithmetic, but the surrounding context is different: checkout may apply promotions, invoices may include display formatting or accounting-specific fields. Forcing both into one giant “OrderProcessor” creates an object that knows too much and becomes a bottleneck.
A more nuanced rule is: extract stable business rules, not incidental implementation shapes. If the same tax or discount calculation appears in two places, that is a candidate for sharing. If two methods simply have the same control flow but one works on an HTTP request and the other on a database entity, the duplication may be harmless or even preferable.
A practical interview-ready example:
Checkout: determine final price for the cart Invoices: determine final price for the line items on the bill Shared rule: orders above the threshold get the same loyalty discount Not shared: checkout also handles payment authorization, invoice also handles document formatting
This is where coupling matters. Extraction gives you a single rule, but it also creates a dependency. If the shared abstraction is poorly chosen, every caller becomes tied to its shape. That coupling is acceptable when the concept is truly shared; it is harmful when the abstraction only exists to eliminate similar lines of code. In other words, DRY is not just about removing repetition; it is about selecting the right level of ownership for a rule.
A good question to ask before extracting is: Will these call sites really change for the same reason? If yes, share the rule. If no, keep the code separate and accept the duplication.
For issued invoices, preserve the prices, discounts and effective rule version captured when the transaction was agreed. Sharing a rule does not mean recalculating a historical invoice using today's discount policy. The order snapshot and the reusable pricing policy serve different purposes.
Failure modes
- Mechanical deduplication: removing every repeated pattern, even when the code is coincidentally similar.
- God abstraction: creating one shared class or helper that bundles unrelated checkout and invoice behavior.
- Leaky reuse: sharing a rule but forcing callers to know too much about internal details.
- False economy: a tiny duplication is kept because it is easier to understand locally; that can be valid, but only if the business rule is not actually duplicated knowledge.
- Overcoupling through helper obsession: placing every shared operation into a common utility class until the code becomes hard to test and harder to evolve.
Interview drill
Use these prompts to practice a strong answer:
- What does DRY mean in terms of knowledge rather than code text?
- When is it better to keep duplicated code instead of extracting it?
- How would you share a pricing rule between checkout and invoices without sharing unrelated workflow?
- What signals tell you an abstraction is too broad?
- How does extraction introduce coupling, and when is that coupling acceptable?
A concise interview answer could be: “I use DRY to centralize business rules like pricing thresholds, because duplicated knowledge is risky. But I do not merge unrelated code paths just because they look similar. If checkout and invoices both need the same discount rule, I share that rule; I do not force payment, formatting, and accounting into the same abstraction.”
Revision checklist
- DRY means one source of truth for duplicated knowledge.
- Similar-looking code is not automatically a DRY violation.
- Share pricing rules when checkout and invoices must stay consistent.
- Do not combine unrelated responsibilities just to reduce line count.
- Remember that extraction creates coupling, so abstraction should match a real business concept.
- Be ready to explain tradeoffs and justify intentional duplication when appropriate.