DRY - Don’t Repeat Yourself
DRY(Don’t Repeat Yourself) aims at reducing the repetition of code chunks and patterns, which was first defined in the book The Pragmatic Programmer as “every piece of knowledge must have a single, unambiguous, authoritative representation within a system.” In this article, we are going to discuss this principle in details.
Define Repetition
When something happens more than one time, it is call repeating. It can be system functionalities, code chunks or certain patterns.
The duplicated code chunks
It refers to the same code snippet that can be found more than once in the code base. Two pieces of code are also considered as the same if the differences are only in the names.
The repeating patterns
Repeating patterns can happen with different values, different types or different behaviours etc. Often we can find we need a common logic to process different values of same type. Or a common logic to process value of different types. For example, in the API code, even though the requests and responses of different api endpoints are not the same, but the pattern of initialising the requests, making the network call and parsing responses are very similar.
Why it is NOT good
Maintenance issue
One obvious issue for having duplicated code chunks/patters comes from maintenance. If duplicated, when the logic requires updating, we would have to update all of them. Not to mention if we have only updated part of them, bugs are inevitable.
Team efficiency
Calling reusable code is definitely easier and quicker than writing the logic ourselves. However, sometime it is arguable if we need to take long time to design and write the code that’s reusable. But considering the other benefits of code being dry, it is worthy for most cases.
Code quality
Once we make effort to write reusable code, it usually ends up with better design and better code quality, which, as we all know, is super important for projects that last for a long time.
Code Consistency
Having code duplication is also harmful to code consistency. For instance, the duplicated code might be the same initially, but they could be updated to a different implementation by other developers later, which then would cause more troubles and impact code readability and reusability.
Techniques for DRY
Function
Many IDEs like(Android studio and Xcode etc.) provide refactor tools to make this process easier. Once the code is abstracted into an individual function, other places can call it to avoid code repetition.
Additionally, when defining the function, design its parameters/arguments with care. A good function signature can make a big difference in terms of code reuse.
In some cases, the place defining the method also plays a critical role in promoting code reuse. For example, we can define toImage() function that returns Image in a class named Rectangle . In contrast we can also add a method getImage(from rectangle: Rectangle) to Image class. It seems that both ways make sense. However, when thinking about code discovery, the former method might be easier to be found and reused by other developers without prior knowledge.
Loop
Believe it or not, loop is actually a very direct and effective way of avoiding repetition. Sometimes, the code logic can be organised in a loop with some tweaks in the loop statements to avoid it. This is effectively true if you are familiar with KeyPath type in Swift , which can make an impossible loop function possible. Take a look at the example below:
struct AStruct {
let a: String
let b: String
let c: String
}
let properties: [KeyPath<AStruct, String>] = [\.a, \.b, \.c]
let value = AStruct(a: "a", b: "b", c: "c")
properties.forEach { keyPath in
print(value[keyPath: keyPath])
}
Code template / Macro
Some languages(like c++) support code template and macro. The code will expand in compile time at the place of using the template and macro. This is similar to abstracting code into functions but in another form.
Macro is a feature in modern languages like Swift. Take a look at this proposal , which is to add macro support in Swift. It has been accepted and will be released in the future version of Swift.
Class, Protocol and Inheritance/Polymorphism
Needless to say, All levels of abstraction like Class/protocol and Inheritance/Polymorphism can help on code duplication to some extent.
Generics
Generics is another way of abstracting common logic to promote code pattern reuse.
Configuration and Data Structures
This is much like design function parameters/arguments, by leveraging configuration and data structures, code logic can be reused for different cases.
Consider using frameworks/libraries
There are a lot of high quality frameworks/libraries that can be reused. Explore them first to avoid reinventing the wheels. The bright side of using them is to save the effort. Especially with the ones that have been proven to be bug free and efficient. Swift Algorithms is an good an example in this category.
Code generation
As long as we can find common pattern in code, as software engineers, we can develop code generation tools to help us. You might already know Swift complier can automatically generate code to make struct type conform to protocols like Codable , Hashable etc. as long as the inner data types conform to these types. Also Xcode can automatically insert the code snippet for struct initialisers when you type in it.
There are many other famous code generation tools out there waiting for use. Like Apollo-codegen, Swagger Codegen, KSP, Jinja, Sourcery, just naming a few. Once configured, they all generate code automatically and save a lot of effort and, of course, help to reduce code duplication.
Conclusion
At last, DRY code plays a crucial role in our daily work and thinking with DRY mindset should be part of our routine. Techniques listed above are not a full list at all. Any level of concept abstraction can help on this and should be encouraged. With this mindset, we can surely improve on a regular basis.
Disclaimer
If you are working on efficiency critical systems(where, comparing code reuse, the real time response is more important) the points here we talked might not applicable.
Sharing is caring!