Do you have to choose between WET or DRY for your code?

AntoineJ
AntoineJ
Dec 29, 2020 · 8 min read
Image for post
Image for post

As a developer we very often hear a lot of acronyms for good practices that often seem like good ideas.

DRY is one of them. This best practice has been described years ago by Andrew Hunt in the famous book Pragmatic Programmer.

DRY means “Don’t repeat yourself” so every piece of your code must be in only one place. It is a prohibition to duplicate

At the opposite, the acronym WET “Write Every Time” or “Write Everything Twice” considers that we can duplicate code without problems.

To these 2 contradictory injunctions, let’s try to see more clearly: DRY seems to be a good enough obvious practice to be respected everywhere. Is it always that simple ?

The objective of this article is to share my opinion on the subject to give a maybe less clear cut :)

How to be DRY?

With OOP languages we have access to more advanced paradigms like inheritance and polymorphism.

We can also use the right design pattern corresponding to our need.

We can also abstract some business or technical logic and package it to share the code with others projects.

DRY advantages

It is much longer and harder to understand a large code base if entire pieces exist in several places, in different forms, sometimes with only little variation.

Over time the duplicated code will diverge more and more, raising the costing of future evolutions.

Moreover the codebase grows unnecessarily, especially if one takes into account the additional unit tests and the possible integration tests.

Reuse

Limit bugs

Maintain quality

DRY problems

Because I think that trying to be DRY at all costs can lead to certain disappointments.

Let me tell you a story:

“It is a classic day in our startup, the business owner of our product comes to us with a need. He explains the what, the why, then it is up to us, the development team, to define the how and how much.

The subject is complex, we discuss between developers and we end up with a solution accepted by all.

The development starts and goes well, it is necessary to regroup some code, to refactor in order to reuse as much as possible the existing one, extract functions, classes, finally everything works perfectly.

Two weeks pass and a new need arises: it is necessary to add functionalities to this module.

We analyze the need, and it appears that it does not fit at all in the box. This request calls into question our previous choices, we must therefore start a refactoring which had already been carried out a few weeks previously in order to connect this functionality to the existing one.”

Image for post
Image for post

Have you ever experienced this situation? probably yes.

“We created the wrong abstraction”

The situation could have been worse where in the urgency of evolution, we would have had to twist the code and complicate our abstractions to the detriment of quality.

It is usual to respond to a need with an evolution that abstracts part of the code to add the new functionality. However, sometimes the new functionality seems close to what existed in the codebase, but it was just an impression. The business need is completely different, and the future evolutions of this functionality will call into question the choices of reuse.

Solutions

Before development

Resist the urge to go into the development and perhaps premature refactoring :

  • Challenge the need
  • Suggest solutions and ask for pain points if it’s does not fit 100% to the primary need
  • Pay attention to the expression of solution rather than an expression of need
  • Ask as many questions as necessary, ask for details
  • Ask how the functionality will evolve, what is planned for the future of this functionality
  • Ask for the vision to your business owner and / or those in the know on the subject

Sometimes it is difficult to get precise answers, don’t hesitate to use the Five Why.

Give you all the cards in your hand so you don’t get one bullet in the foot :

“Create abstractions create future limitations”

This does not mean that abstractions are to be avoided, but only : beware to silly refactoring and thickheaded attempts to make generic code.

When development begins

First we try to detect if there is a completely wrong abstraction in our code ; or maybe an abstraction to adapt. If there are a lot of parameters on a component, it’s usually a bad thing. It is important to analyze the code and try to determine if a refactoring is necessary.

At this step, be careful of premature optimization. Do we have enough perspective to design something in an higher level?

“A good design is the one with the least comitment” Robert C. Martin

I observed that it often takes at least 3 different contexts for a feature to have the necessary perspective allowing to get to the right level.

Now is the time to make the right choice. Depending on the analysis, not being 100% DRY is perfectly acceptable.

This is called the chosen technical debt and it is perfectly normal on some projects.

The most important thing is to document the technical debt in an ADR, take it into account for the next development on this part of the code base and do not forget to buy it back.

Examples

DTO management

These objects are DTOs and we have chosen to isolate these contexts with distinct classes, therefore by duplicating certain fields. Reuse is partial on these classes. This design is chosen, is not a pain point on a daily basis has often allowed us to adapt specificities very easily.

Duplication is part on our design and is not technical debt.

Wait and learn

In this case, I think it was a good idea. On the other hand, once we have learned enough, we should not delay in merging these codebases. The difficulty lies in finding the right moment and the right abstraction, at the right level.

If we wait too long, too much time and energy is spent maintaining and upgrading these codebases. If we don’t wait enough, we may not have learned enough and therefore create a bad abstraction.

This is typical technical debt and must be redeemed at the right time.

Frontend component

It’s a middle ground, a chosen technical debt part of our conception. We know it’s not perfect but we live with it without pain.

Conclusion

I think It is a mistake to worship some concept and use it as an absolute truth.

I prefer search the happy medium and always challenge the “fit them all” solution.

Abstractions and generic code are useful but beware of future limitations.

Remember: “the best is the enemy of good” and do not be blinded by ready-made sentences ;-)

#Pragmatism

Thanks for reading

_______________________________________

Sources :

YounitedTech

Le blog Tech de Younited, où l’on parle de développement…

Thanks to Slim Ayache

AntoineJ

Written by

AntoineJ

Lead Dev @Younited-Credit

YounitedTech

Le blog Tech de Younited, où l’on parle de développement, d’architecture, de microservices, de cloud, de data… Et de comment on s’organise pour faire tout ça. Ah, et on recrute aussi, on vous a dit ?

AntoineJ

Written by

AntoineJ

Lead Dev @Younited-Credit

YounitedTech

Le blog Tech de Younited, où l’on parle de développement, d’architecture, de microservices, de cloud, de data… Et de comment on s’organise pour faire tout ça. Ah, et on recrute aussi, on vous a dit ?

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store