Dont Repeat Yourself

  • Repeating yourself, means wasting time, time that could be used to do other stuff

  • DRY is about wasting time

  • Focus on not repeating the business logic, this is should be one place

  • Areas of wasting time

    • Following an inefficient process

      • This can occur in the release cycle, the way new features are designed, sign-offs, or meetings

        • Use feedback, retros, to find out the waste and improve processes

        • automate these processes

      • Hearing we have alwasy done it, is a sign of inefficiency

    • Lack of automation

      • ie

        • deploying manually

        • compiling

        • testing

        • building

        • configuring development machines

        • provisioning servers

        • documenting APIs

      • Try to automate your builds and deployments from day one, as they will only get more complicated as you go along

    • Not invented here, also known as reinventing the wheel

      • arising from writing code before considering the reuse of existing code

      • people who enjoy implementing things that are easily available (inhouse or in open-source world)

      • Always search for libraries which do what you want

        • but make sure it can do exactly what you need, and if not it can be configured, do spikes

    • Copy/paste programming - this is a common misconception of DRY addressed by the authors of it

      • Leads to errors

      • Leads to multiple places where changes need to occur

    • “I won’t need it again so let’s just hack it quickly” solutions

      • Never good if not tested

  • Not doing so means changing in one place, means having to remember to change in other places

  • Copy and Paste Programming

    • the more code you write, the more expensive it becomes to support and maintain the application

    • Both composition and inheritance are your friends in battling repetitive code

    • the use of design patterns and shared libraries.

    • web services to combat duplication on higher levels of abstraction.

      • Instead of building the same functionality into each application you develop, it is often a good idea to create a service and reuse it across the company.

Examples

  • Libraries

    • but can lead to coupling

When to use

  • Better to do it after it is apparent

    • Not extract when no duplication occurs in the hopes of reusue

    • Three occurrences and then dry

    • If it is known that the future requirements will be similar than dry

  • When it is regarding source of knowledge, not just patterns

  • Benefits outweigh the negatives, especially the time investment

  • It follows the idea of ETC (easier to change), it makes it:

    • easier to read

    • faster to extend or change

Acid Test

  • the acid test: when some single facet of the code has to change, do you find yourself making that change in multiple places, and in multiple different formats? Do you have to change code and documentation, or a database schema and a structure that holds it, or…?

  • Best to time box the drying up of code, and see if it improves the codebase (ie ETC), and revert if not

Types

  • Imposed duplication.

    • Developers feel they have no choice

    • the environment seems to require duplication.

  • Inadvertent duplication.

    • Developers don’t realize that they are duplicating information.

  • Impatient duplication.

    • Developers get lazy and duplicate because it seems easier.

  • Interdeveloper duplication.

    • Multiple people on a team (or on different teams) duplicate a piece of information

Pitfulls and issues

  • A little duplication is often better than a little dependency.

  • Prematurely optimize when the requirements aren't finalized (spoiler: they never are).

    • Hence, the abstraction may need to change to cater for different cases for different consumer needs

    • https://overreacted.io/goodbye-clean-code/

    • https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction

  • Duplication is a convenient but not the best source for discovering abstractions

  • wrong abstraction at a lower level creates an exponentially worse dependency hell at higher levels.

  • There is always a trade off

    • sometimes better to copy and paste, and minimal changes, instead of having lots of logic

    • Time to refactor might be too long to remove duplication, and changing other code will require retesting

    • To much dry code can result in complex code, too much abstraction and hard to understand

  • Removing Duplication Requires Abstractions and Abstractions Breed Complexity

    • Such generalized abstractions are notoriously hard to test and understand because they must handle many more use-cases than the original (potentially duplicated) code.

    • Said another way, abstractions support more behaviors than might actually be needed for the system to function properly.

    • Thus, the removal of duplication can introduce new behaviors to the system that aren’t required.

    • Duplicated code tends to also obscure the structure and intent of your code, making it harder to understand and modify

    • If an abstraction exists, then future change will feel the need to use this abstraction ( and thus change it) which leads to pollution of ideas and complexity

  • This was first promoted in Pragmatic Programmer (Thomas)

    • In the second edition, they realised that it was not the correct message, as people took it too far

      • people believe that no copy and pasting was allowed

    • Instead Duplication of business logic should not be repeated, there should be a single source of truth

    • DRY is about the duplication of knowledge, of intent. It’s about expressing the same thing in two different places, possibly in two totally different ways

    • Dont dry coincidental duplication only essential duplication

      • essential duplication are parts that should change together

  • When a duplication is abstracted, and changes are needed for only on consumer of this abstraction, this can lead to clauses (if statements) proliferating the code base

    • instead duplicate the code

  • Lead to tight coupling

    • prefer some duplication instead

  • DRY can make it harder to debug

    • Should think very carefully to do this in test code otherwise can be hard to follow intent or debug

  • For small amount of duplication is not worth it

  • DRY can be seen as premature optimisation

  • https://www.infoq.com/news/2012/05/DRY-code-duplication-coupling/

  • https://oncodedesign.com/dry-vs-coupling/

  • https://www.industriallogic.com/blog/spot-and-coincidental-duplication/

  • https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction

  • https://www.entropywins.wtf/blog/2017/09/06/the-fallacy-of-dry/

  • https://rotemtam.com/2020/05/18/the-dry-principle-is-bad-advice/

  • https://gordonc.bearblog.dev/dry-most-over-rated-programming-principle/

  • https://salmonmode.github.io/2020/08/14/the-harmful-obsession-with-dry.html

  • https://www.gordoncassie.com/dry-most-over-rated-programming-principle/

    • https://www.gordoncassie.com/more-dry-talk-responding-to/

  • https://lbrito.ca/blog/2017/03/dont-obsess-over-code-dry.html

  • https://swizec.com/blog/dry-the-common-source-of-bad-abstractions/

Issues with many small methods

  • Implementing DRY can lead to creating many methods, and the use of the popular refactoring Extract Method.

  • Sometimes it is better to fold the mehtods in (Inline Method)

  • writing functions that are nothing but 50 functions calls that themselves are just a few lines of code. That's as bad as the people who write 1,000 line functions

  • This can cause issues:

    • Lots of little methods makes code get hard to maintain

    • costs to breaking code up:

      • more cognitive load when a reader must jump around a file and remember what different functions do

        • Logic is split apart and harder to follow

      • too many functions means more lines of code that makes it harder to see more of the code on your screen at once

      • more functions means more argument and return value passing which increases the chance for bugs

      • separating two very related pieces of functionality (get next free id and update next free id) increases the chance for bugs when someone updates one piece of the functionality and doesn't realize they need to update other related functionality in a different function.

        • issue of temporal coupling, calls need to be together if they are dependent in order

      • pulling more functions out makes it more likely that someone will reuse that function for something else which builds up complex dependencies making it harder to change some of the code because you have to understand much more of the codebase to safely change the code.

  • https://www.reddit.com/r/ExperiencedDevs/comments/u7xynw/many_small_methods_make_code_hard_to_maintain/

Keeping long blocks of code managable

  • long blocks of code (within reason) are made much easier to consume when they are broken up and commented well. I approach it but looking at the logic flow, then separate lines and blocks of code that are discrete logical steps in that flow, comment each, and only THEN ask myself what would make it better as another functio

DRY vs Separation of Concerns

  • Forget if the code looks similar. Is it describing the same business concept? If yes, DRY. If no, separate.

  • https://swizec.com/blog/dry-vs-soc-a-difficult-choice/

DRY vs YAGNI

  • https://swizec.com/blog/dry-is-a-footgun-remember-to-yagni/

Last updated

Was this helpful?