Apply DRY to knowledge, not source code
Tags: programming, architecture
Published: 2022-08-21 07:05:31

DRY or Do Not Repeat Yourself, is an often repeated important concept in programming. Most of us try our utmost to prevent writing repetitive code using methods like abstraction to reuse code across our codebases.

However, I'm sure all of us has come across situations where the code is over-abstracted. For instance a function shared across multiple different areas of code. The logic is perhaps 60-90% the same. However this function is massive with multiple case scenarios that do all manners of different logic with the last 10-40%. We know deep down something is wrong, but hey - we shouldn't repeat code right?

Wrong.



I've started reading the Pragmatic Programmer and I've finally been able to put my finger on the reason why this common scenario feels so wrong.

As programmers we focus on source code and making sure we DRY our code. But DRY wasn't supposed to be for source code. It is supposed to prevent code with the same knowledge being repeated. There is a subtle difference, but it's a definitive one.

## The problems of focusing on DRY for source code in isolation

Some code may have similar internals or even portions which are identical. But these differences may be coincidental. 

For example code for logging in for a buyer user seems very similar to logging in for a seller. The code might even be 90% similar. But because of the 10% differences the actual knowledge being captured is significantly different. If you simply add many case scenarios (using switch cases or `if` statements) you are creating code that will be very difficult to debug due to being tightly coupled. Also what happens if you add a third type of user and a fourth such as an auctioneer and agent?

I've added a quick example below

```
const logInUser = (invitationType, user) => {
    const message = `Hello ${user.userName} from ${user.org}`
    const locale = user.locale

    if (invitationType === 'buyer') {
        // Get data from buyer member table and set state
        // Make API call to get list of possible purchase options to display on welcome page
    } 
    if (invitationType === 'seller' {
        // Get data from seller member table and set state
        // Make API calls to get summary of buyers interested to notify user
        
    }
}
```

You should instead focus on applying DRY to knowledge. If the  requirements, flow and needs are different it requires different code even though portions may seem to be DRY. Focus on the big picture. If you apply DRY dogmatically to source code you will make tightly-coupled code which should not be coupled.

## Some disclaimers

Of course there are ways you can share code without coupling in the above situation. You can have two separate functions dealing with each legitimately different flows. You can then safely abstract parts of shared code that doesn't couple the two different scenarios together. Then both code blocks that are capturing different knowledge can safely share this generic code.

eg. 
```
const generateMessageForInvitedUser = (user) => `Hello ${user.userName} from ${user.org}`

```

## Conclusion
Don't dogmatically apply DRY to source code. Have a think about the knowledge that a code block captures. Make sure to apply DRY to code that repeats knowledge. However if some source code is similar or has identical parts, check whether these two different code blocks are capturing different knowledge if so think twice before applying DRY.