New Year, new you, right? 2021 was a bit of a slog for us all and as we approached the end of the year it was easy to fall into bad habits. Why not begin 2022 with a clean start – have a DRY January!
The end of year release (or worse, December code freeze) generally causes a last-minute rush of work to be crammed into the last few weeks. It’s in this rush to the finish line where corners start to get cut and good practice fades away at the expense of feature delivery – we promised this by the end of the year – just get it working can be heard in many organisations across the land!
And sure, you got it working, but as you go back to your codebase after the Christmas break, December has faded in the memory and you’re probably left asking yourself how does this code work?!? Once again we’ve compromised on quality to get something out of the door adding in a little more technical debt.
In the spirit of the new year, let’s try and set things right by having a DRY January. Now I’m not talking about an alcohol free January here (of course you are free to do this) – I’m talking about a January where we take to heart the DRY principle of Don’t Repeat Yourself! How hard can that be? The DRY principle is just about not having duplicated code, right? Well – maybe it’s time to look a little deeper at this principle.
DRYK?! Don’t Repeat Your Knowledge isn’t as catchy!
You can understand why this principle is misinterpreted – Don’t Repeat Yourself seems plain and simple. Many developers coming through our Certified Scrum Developer, SAFe Agile Software Engineer and Engineering for Agility classes have never gone beyond the DRY acronym – they were not aware that it’s actually an oversimplification of a more nuanced design principle. The principle states (from The Pragmatic Programmer):
The key word in this principle is knowledge. In your applications, knowledge is typically represented as algorithms, calculations or rules.
DRY is not simply Don’t Repeat Yourself – it depends what is being repeated. If you are repeating knowledge (rules etc) then you are violating the principle. But if you are repeating code (e.g structure) without repeating knowledge, then you are not violating the principle – you might be able to write your code better, but you’re not violating DRY!
Here’s an example from a fitness app.
In this view model we format the start time of an activity. We do the simplest thing that can work.
As we add more detail to the app, we start to show more stats. Now we want to show the fastest lap time. Time should be presented in a consistent way, so we can just copy and paste the formatting code into a new function right?
The way that time is formatted in the app is knowledge, and it shouldn’t be duplicated like this. Instead we should move the formatting logic for time into a single place that can be called appropriately.
This example shows a simple case but the reality of the app is that time is displayed in a lot of places – the formatActivityTime function is actually declared once in a Formatter class that is used by the whole app.
Imagine if we have to change the way the time is displayed in the app – e.g strip the leading zeros. If we’d carried on duplicating the formatting code, we’d need to make changes in a lot of places! Chances are we’d miss some too. By having the formatting knowledge in a single location, we can make that change very quickly and feel confident the change is complete.
We’ve also simplified our testing too with this approach. Everywhere we duplicate the formatting we should really have a test of the formatting. A change to the formatting would mean a change to a lot of test cases also. With the refactored code, we’d have a test for the Formatter class/struct/function to check the display time format and wherever the formatter was used we could use a mock/spy to simply check that the code called the formatter appropriately. In that instance, when stripping leading zeros we’d only have to update one test case.
The example given here appears trivial, but what generally happens is we duplicate the code a few times for speed of development, then when we get to the point where we think “I really should refactor this” we’ve got a lot of duplication to refactor so we don’t bother! I know this, because this is my code and that’s exactly what happened!
Let’s look at another example of code with some very similar looking code.
In this second example we have a block of code with very similar looking constructs! Could it be more efficient? Maybe! But is it duplicating knowledge? No. The code is similar but the DRY principle is not being violated here.
Sometimes applying DRY to this sort of code ends up over complicating things. The code becomes less readable for the sake of efficiency or brevity.
Use your knowledge wisely!
So you understand now that the DRY principle is about knowledge – but, be careful with your new found knowledge! Only apply this principle when you have a demonstrable need to! The world is full of over-complicated codebases that try to anticipate every reuse scenario – I know, I’ve contributed my fair share! An over-abstracted codebase is an over-complex codebase and that makes it less readable and therefore less maintainable. Only apply this principle (and all the other good ones out there) as you need them.
Finally don’t forget that DRY is just a principle – it’s not the law! Treat it as a heuristic – a technique that is not perfect but is generally good enough. If you need to have knowledge in 2 places and to do that is easier than refactoring a design, you might choose to go with the duplication and some suitable comments in the code. Martin Fowler says that you need to take into account the effort of refactoring vs the effort of maintaining the duplicated code when applying this principle. This can be a slippery slope though – if you chose to duplicate but get that funny feeling, maybe you should refactor!
The DRY principle and the Single Responsibility principle are closely related – if you apply both of these sensibly in your codebase you will have, in my opinion, the foundations of design.
If you’re interested in learning more about other principles, patterns and approaches to agile software development, why not check out our agile development courses:
- Certified Scrum Developer
- Advanced Certified Scrum Developer
- SAFe Agile Software Engineer
- Engineering for Agility
If you want to try an alcohol free January, you can find details here.
This blog was inspired by a tweet from @iamdeveloper.