The DRY principle is a software development principle that stands for “Don’t repeat yourself.”
It means there should only be one copy of any vital piece of code in your codebase, every other area where it should appear should bear only the instance of the main code.
The goal is to reduce the repetition of code and code redundancy.
The DRY principle suggests that every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
Knowledge can be:
- A precise functionality in the codebase of your application.
- An algorithm.
This principle aims to reduce patterns’ repetition and code duplication to avoid abstraction and redundancy.
There are many reasons and benefits for this, but most importantly, one copy of code is much easier to maintain than multiple copies of the same piece of code that are functionally the same.
If there’s any need for the code to be changed, then only one occurrence will be worked on.
DRY is a long-standing principle in computer science, and it’s the idea behind most of the latest web development technologies, libraries, and frameworks.
If “DRY” means “don’t repeat yourself,” the corresponding “WET” principle which stands for “write everything twice.”
Thus making maintainability harder.
Changes in the code will have to happen in many places instead of one.
The DRY principle brings its solution by putting functions and methods in place of all repeated codes and then defining the code in one function.
So by calling the methods, we will reach the principle DRY.
On the basic level, here’s an example: Taking repetitive bits of code and extracting them into a function.
Benefits of Applying the DRY principle to your code
This benefit is the most profound advantage DRY brings to your code.
Maintaining a codebase is more work than creating it, and we employ whatever strategy that can simplify that for you and use it to your advantage.
In this article, our example will be based on a Reactjs app whose state is being managed using Redux.
You have already set up the logic of checking user authentication and user authorization.
If the implementation of this logic doesn’t embrace the DRY principle by using Redux to its full potential, the logic will be complicated to debug and fix bugs.
It will require us to make the same request to our database from inside several components and repeatedly do the same authentication checks on several pages that the app should block unauthenticated users from accessing.
In a huge app, you could forget the number of areas these codes were implemented and spend time filtering through different lines of JSX codes in search of their occurrences.
Again, if you have to fix the bug, you must copy the correct code and paste it on several occurrences, irrespective of how many.
You will agree that this is not a good way to use your development time.
By Dry-ing up your codebase, you only have to maintain the code in one occurrence, which will fix every other bug caused by that faulty code.
In most cases, when you have a new logic to add to the existing code, you only need to update its only occurrence, which will add this logic to every other instance of the code.
This will lead to an efficient, robust, and dependable codebase.
Looking at code and trying to decipher and connect what each part does to another part is already a difficult task.
One which its difficulty is more profound when you were not the one that wrote the code or when the developer did not comment on the code properly.
A codebase written with the DRY principle in mind is more readable.
This is because of the measures needed for the DRY principle to be used on a codebase.
For a developer to follow the DRY principle, they must put in efforts to organize code which aids code readability.
You want to write code once and use the instance of that code in multiple places, promoting code reuse.
This benefit is achieved by merging two blocks of repeating code into a single block of code.
Most importantly, this helps decrease development time.
Cost of Development
While you reduce the size of your codebase using the DRY principle, you are optimizing many things alongside it.
A large codebase requires more time to create, takes more effort from more team members, takes more time to maintain, and is more frustrating to deal with bugs.
An extended development time and an increased number of bugs after deployment lead to a displeased client or a disappointed project manager.
Testing is a very important process in the life-cycle of a project’s development phase.
The more paths and functions you have to test, the more testing code you have to write.
When you create your codebase using the DRY principle, you only have to test only the main paths.
Overusing the DRY Principle
Knowing when to use DRY and when not is the key to writing clean, reusable, and robust codes.
Every line of code doesn’t have to be unique.
Sometimes duplication might suit the context and DRY-ing that up might introduce unnecessary complexities into the codebase.
So, how do you know when to use the DRY principle or just go along and repeat yourself?
To understand this, you have to first understand how clean code works;
Your code can gradually grow out of order.
It becomes messier and messier as time goes by, most times without you knowing.
But, it’s your job to adopt good practices to keep the codebase clean and maintainable.
Clean code is much easier for humans to read because it uses good and relatable names for its functions and variables.
It also has less complexity, i.e., has fewer conditionals, because our brains are limited to dealing with complex scenarios.
With that said, let me introduce you to SOLID.
This is a simple acronym for five principles or guidelines that can help you as a developer write clean code that’s easy, maintainable, and reusable.
In SOLID, each letter is important, but they can be identified and practiced separately.
S – stands for “single responsible principle” [SRP].
Codebases written with this principle comply with and tend to be cleaner.
This is because each unit(functions, methods, classes, packages, etc.) specializes in doing just one job.
In the case of changes or fixes, you will know the exact place to look first.
For example, a method that selects an element AND uses that element to do something could be broken into two methods, one that selects and the other that acts on the selected item.
Using this principle, you end up with fewer bugs.
You can understand your code easily as there is less code to be read.
Your codebase is much simpler to compose, and thus good candidates to DRY the codebase.
O – stands for “open-closed principle” [OCP].
This principle has to do with units being open for extensions but closed for modifications.
This means that classes, modules, functions, and packages should allow their behaviors to be extended without modifying their source code.
If you want to implement a supported change on these units that make up your codebase, you should be able to do it without changing the code in many places.
L – stands for “Liskov’s substitution principle” [LSP]
In object-oriented programming, this principle states that functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
LSP is about interfaces and contracts, as well as how to decide when to extend a class or not.
Using an analogy, we all know an Ostrich is a bird, but it’s a bird that can’t fly.
Then in our program, an ostrich is a subtype of the class “bird.”
Meanwhile, flying is a defined method in the bird class.
In ISP, the subtype ostrich should not be made to use the “fly” method defined in the class bird.
If that happens, you will be breaking the LS principle.
This principle helps you model good inheritance hierarchies.
I – stands for “interface segregation principle” [ISP]
This states that a client shouldn’t be forced to depend on or implement methods or interfaces it does not use.
Clients here refer to web pages, mobile apps, other systems, or developers.
In ISP, you design interfaces which give your clients only what they need instead of giving them a general-purpose interface.
For example, since a customer can order burgers, fries, or both, then we decide to put the options to place all orders on one single interface.
This principle helps reduce the side effects of using larger interfaces by breaking application interfaces into smaller ones.
Units of your codes that comply with ISP clearly define what they were designed to do.
D – stands for “dependency inversion principle” [DIP]
This principle states that high-level modules should not depend on low-level modules for implementation, instead should rely on their abstraction.
Understanding the SOLID principles will help you decide when to split your functions and methods into smaller, reusable functions so that you can see portions of your code that can benefit from the DRY principle.
This article on FreeCodeCamp explains the concept of SOLID
Clean and maintainable code is something we all desire as developers, and there are lots of coding principles that promise to help you achieve that – DRY being one of them.
In the early stages of development, it is very easy to ignore this principle. Still, as your codebase increases, you will start discovering how useful these principles are and how they can help your coding journey and, at the same time, benefit the final product.
Hence, the effort you put in to ensure your project complies with these principles certainly pay off in the end.