From 149b8cfdedb66cb5053acfd1078b75558a7c52f2 Mon Sep 17 00:00:00 2001 From: Dennis Doomen Date: Wed, 25 Mar 2026 07:11:33 -0700 Subject: [PATCH 1/2] Update AV1580 guideline Split from #298. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- _rules/0125.md | 34 ++++++++++++++++++++++++++++++---- _rules/1580.md | 30 ++++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/_rules/0125.md b/_rules/0125.md index 80f54c34..b6085167 100644 --- a/_rules/0125.md +++ b/_rules/0125.md @@ -1,13 +1,39 @@ --- rule_id: 0125 rule_category: general -title: Don't Repeat Yourself (DRY) within boundaries +title: Don't Repeat Yourself (DRY), but only within boundaries severity: 1 --- -Avoid duplicating logic, knowledge or decisions within a component, service or [bounded context](https://martinfowler.com/bliki/BoundedContext.html). When the same logic exists in multiple places, a change in requirements requires multiple edits, and inconsistencies creep in over time. Use the [Rule of Three](https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming)) as a practical guide: refactor duplication after the third occurrence. +Avoid duplicating logic, knowledge or decisions within a component, service or [bounded context](https://martinfowler.com/bliki/BoundedContext.html). When the same logic exists in multiple places, a change in requirements requires multiple edits, and inconsistencies creep in over time. -**Exception:** -Duplication across architectural or team boundaries is sometimes preferable to coupling. If sharing a piece of logic between two separate modules or services would require introducing a shared dependency, consider whether duplicating the simple logic is the lesser evil. Tight coupling across boundaries is harder to manage than a small amount of redundancy. +However, although the DRY principle (Don't Repeat Yourself) is valuable within such a boundary, blindly applying it across module or service boundaries can introduce coupling that is harder to manage than a small amount of duplication. + +If sharing a tiny piece of logic between two separate modules would require: +- Adding a dependency on a shared library that both modules must coordinate upgrades for. +- Exposing internal types or contracts across a boundary. +- Introducing a third project or package just to hold a few lines of code. + +…then duplicating the logic is often the simpler choice. + +```csharp +// In Module A +private static bool IsValidEmail(string value) + => value.Contains('@') && value.Contains('.'); + +// In Module B — a small duplication is fine here +private static bool IsValidEmail(string value) + => value.Contains('@') && value.Contains('.'); +``` + +This applies primarily to: +- Small utility functions (a few lines of code). +- Logic that is stable and unlikely to change frequently. +- Logic that doesn't carry domain meaning that must remain consistent. + +For complex or domain-critical logic that must be consistent everywhere, a shared library is still the right choice. Consider the [Rule of Three](https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming)) as a practical guide: refactor duplication after the third occurrence. + +**Note:** +Taking a dependency on a source-only package like [Reflectify](https://github.com/dennisdoomen/reflectify?tab=readme-ov-file#readme) or [Pathy](https://github.com/dennisdoomen/pathy?tab=readme-ov-file#readme), or putting your utility code in a source-only package also avoids coordinated updates. But duplication may still be the safer choice. **Exception:** Duplication in tests is often beneficial as it will make the tests easier to understand without the need to dig into all kinds of shared helper methods. diff --git a/_rules/1580.md b/_rules/1580.md index cb1b8b13..7387174b 100644 --- a/_rules/1580.md +++ b/_rules/1580.md @@ -1,13 +1,31 @@ --- rule_id: 1580 rule_category: maintainability -title: Write code that is easy to debug -severity: 2 +title: Consider duplicating simple logic across modules to reduce coupling +severity: 3 --- -Because debugger breakpoints cannot be set inside expressions, avoid overuse of nested method calls. For example, a line like: +The DRY principle (Don't Repeat Yourself) is valuable within a bounded context, but applying it blindly across module or service boundaries can introduce coupling that is harder to manage than a small amount of duplication. - string result = ConvertToXml(ApplyTransforms(ExecuteQuery(GetConfigurationSettings(source)))); +If sharing a tiny piece of logic between two separate modules would require: +- Adding a dependency on a shared library that both modules must coordinate upgrades for. +- Exposing internal types or contracts across a boundary. +- Introducing a third project or package just to hold a few lines of code. -requires extra steps to inspect intermediate method return values. On the other hard, were this expression broken into intermediate variables, setting a breakpoint on one of them would be sufficient. +…then duplicating the logic is often the simpler choice. -**Note** This does not apply to chaining method calls, which is a common pattern in fluent APIs. +```csharp +// In Module A +private static bool IsValidEmail(string value) + => value.Contains('@') && value.Contains('.'); + +// In Module B — a small duplication is fine here +private static bool IsValidEmail(string value) + => value.Contains('@') && value.Contains('.'); +``` + +This applies primarily to: +- Small utility functions (a few lines of code). +- Logic that is stable and unlikely to change frequently. +- Logic that doesn't carry domain meaning that must remain consistent. + +For complex or domain-critical logic that must be consistent everywhere, a shared library is still the right choice. From 438150bb56e7a9fed3b3a71b544fcc86cb3b4407 Mon Sep 17 00:00:00 2001 From: Dennis Doomen Date: Sun, 12 Apr 2026 09:10:44 +0200 Subject: [PATCH 2/2] Remove AV1580 change from PR 298 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- _rules/1580.md | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/_rules/1580.md b/_rules/1580.md index 7387174b..cb1b8b13 100644 --- a/_rules/1580.md +++ b/_rules/1580.md @@ -1,31 +1,13 @@ --- rule_id: 1580 rule_category: maintainability -title: Consider duplicating simple logic across modules to reduce coupling -severity: 3 +title: Write code that is easy to debug +severity: 2 --- -The DRY principle (Don't Repeat Yourself) is valuable within a bounded context, but applying it blindly across module or service boundaries can introduce coupling that is harder to manage than a small amount of duplication. +Because debugger breakpoints cannot be set inside expressions, avoid overuse of nested method calls. For example, a line like: -If sharing a tiny piece of logic between two separate modules would require: -- Adding a dependency on a shared library that both modules must coordinate upgrades for. -- Exposing internal types or contracts across a boundary. -- Introducing a third project or package just to hold a few lines of code. + string result = ConvertToXml(ApplyTransforms(ExecuteQuery(GetConfigurationSettings(source)))); -…then duplicating the logic is often the simpler choice. +requires extra steps to inspect intermediate method return values. On the other hard, were this expression broken into intermediate variables, setting a breakpoint on one of them would be sufficient. -```csharp -// In Module A -private static bool IsValidEmail(string value) - => value.Contains('@') && value.Contains('.'); - -// In Module B — a small duplication is fine here -private static bool IsValidEmail(string value) - => value.Contains('@') && value.Contains('.'); -``` - -This applies primarily to: -- Small utility functions (a few lines of code). -- Logic that is stable and unlikely to change frequently. -- Logic that doesn't carry domain meaning that must remain consistent. - -For complex or domain-critical logic that must be consistent everywhere, a shared library is still the right choice. +**Note** This does not apply to chaining method calls, which is a common pattern in fluent APIs.