Code reusability is the holy grail for software development. You might have heard that developers should write code once and reuse it everywhere. Or, never write the same code twice… and a lot of things on similar lines.
But have you ever questioned this?
Is redundancy such a bad thing after all?
Don’t you think these statements are subjective? Applied to specific use cases.
In this article, I want to share my thoughts on code redundancy and what I have seen in my career so far that worked well and well, not worked well 😀
Let’s try to pin down the drawbacks of having code reusability.
Table of Contents
A Closer View On Redundant Code
Don’t you think common code creates dependency? When we write the same code in two places and extract it out into a separate function, then two places are dependent on that code. That code that was flexible to change before is now incapable of that without breaking something.
Making code reusable will introduce unnecessary/necessary coupling and dependencies that if not married wisely will be problematic.
You see whenever you use a code in more than two places, you create a dependency on that code. Now, that common piece of code cannot change freely. It has to take care of all the dependent modules. That’s why all sorts of different design patterns come into the picture. If you look at all the different design patterns, what do they do? They all just try to make code less coupled, less dependent, less fragile.
Let’s say you have a common code that is being consumed from 4 different places:
And then requirements changed for component D. It needs a change of logic on the dependent code. Then some smart person stepped in and introduced interfaces
or abstract
classes. The role of this glue is to keep the contract same but enable different components to inject different implementations based on the requirement. Now we all know this as the inversion of dependency.
If you look closely. This is not a very elegant way. You need some kind of factory/injector that would hold the logic for creating the required object and providing it to the downstream components. Since this was getting nasty, smart people had to interject again and provide a framework for this.
Spring which is the best framework I think is the best framework for Java for creating mostly web applications provides a simple means of doing this injection. At some level, all it does is the injection of dependencies to classes. Called as Autowiring
. The term is so fancy that it took me a day to understand what is this magic keyword. But it’s just that Spring creates a middle layer that provides the dependency to the required classes.
After some time the testing became difficult because developers simply started using @Autowire
everywhere in the code. Okay, spring will provide the dependency at runtime for you, but how are you going to provide the dependency while writing unit tests. Then I have seen power mockito and all sorts of stuff to mock objects and do whitebox
and what not to mock private methods and perform testing 🤮
Then came different ways to Autowire
this object.
- Autowire via constructor
- Autowire by type
- Autowire by name
Which is nothing but ways to inject dependency into the class. And finally, developers united on autowire by constructor as the best way to autowire dependency because then it allows for injecting dependencies via test classes without requiring additional mocking framework.
Oh, I just realized we started just by decoupling a simple piece of code… ended with types of autowiring. So, you see a lot goes into removing redundancy.
And while we are on the topic, I want to tell you that I have seen code that introduced 6 classes with all sorts of abstractions just to remove the 4 lines of redundant code in classes that doesn’t even share the same context. Well, I could live with redundancy as long as they are never going to cross the boundaries.
I learned to not have a mindset that says – “copying code from one place to another is redundant therefore introduce abstraction”.
Read the situation. Sometimes it’s a lot better to be redundant. From my experience, redundancy is a lot better than introducing complicated abstraction that makes the code hard to read and difficult to modify. Again, read the situation.
Bundle Common Code Into A Library Creates Dependency
Another thing that I have seen is the bundling of the common code into one package.
I have literally seen big massive jars starting by the prefix common-{}
. These massive jars had nothing in common rather specific stuff from all the different projects across the organization. So, let’s say you are working on a module that has a piece of code or class that is required from more than one place in your module. Then it was added to the common jar/package.
Then this introduces another problem of supporting different versions. It could happen that one team introduced new “common” functionality, then they generate a new bundle and increment the version. Now, all teams in the organization start using their own version of “common-{}” jar. That has nothing in common 😀
Amazing isn’t it?
Ever been a part of “The Deployment Day”. It’s a real thing. And it’s daunting. Well, it’s not just because of the common code, but common code plays a huge role in this. A week before the deployment day all the code across all teams are frozen. You need additional permissions to change after code freeze. A big excel sheet is distributed across all teams where each team enters their dependencies versions to deploy to production. I don’t even want to talk about it, it’s so depressing. Let me check if I have written something on it somewhere that I could share. (found it but this deployment day was much better than the one I’ve experienced before in my first organization).
Eric Evans – Domain Driven Design
I think Sir Eric Evans decided to come up with DDD because he was done with all the shit he saw.
There he tried to explain that redundancy is good as long as it respects the boundaries. Okay, he didn’t say that, it’s just me manipulating his idea to strengthen my point.
But he did mention the example where he said – Having identical classes with the same properties and even the same name is cool if they tell a different story. It’s very much like a conversation. Two identical sentences can have a completely different meaning if the context is changed.
Because of such beautiful ideas, I say that code shouldn’t be looked at as poetry but as a wonderful conversation between two people in context. If you try to look at code as poetry then only you understand it. But if you look at a code as conversation, you make sure that you put your intent out in the world. And DDD bridges this gap.
Now I will share an example from his book.
Just to be clear, I’ve not read the entire book, but I have read different sources and watched youtube seminars talking about DDD that I think I got the gist. And I really like the idea of looking at the code as separate domains with context and intent. Again it is not to be taken blindly – read the situation. Code comes naturally.
Let me try to create a visual representation in my own way:
On the left is a person who hates redundancy and on the right is a person who embraces redundancy as long as it is provided with a beautiful context.
On the right, the person has decided to declare clear contextual boundaries. And a piece of code used in the particular module can be copied and pasted to another module, no problem. It’s the same situation, where the sentence is the same but the context has changed.
C1M1,.., C5M5 doesn’t give a shit about any other module. Therefore, they don’t care if some code is copied.
Another thing to notice is that classes are closed and secured in their own package with a single entry point. Whereas on the left all the classes are public. I have seen structures like service/impl
where impl
package contains the implementations for the different interfaces that are put in the services
class. I have even done this sometimes.
When I asked this to my lead like years ago (when I was a fresh graduate who wrote redundant code and embraced the feature of CNTRL + C and CNTRL + V) – why are we creating an interface for every class? I got the answer that it provides flexibility. My immediate question was how? The answer was that if in the future a different implementation is required then we won’t have to change the contract. I waited for 2 complete years to see what kind of requirement will change the implementation but not the interface. 2 years into the future, I called my colleague who was still working in the same team and I asked him, what happened to that module. Trust me, the future is yet to come.
Quick Example To Explain Where I Like To Use Interfaces
Imagine a scenario where there are multiple events coming at you. And you have to process each event separately. There are two ways to solve this:
If/else – switch-case
class Event {
string type;
EventData data;
}
Event event;
if (event.type == "A") processor1.execute(event);
if (event.type == "B") processor2.execute(event);
if (event.type == "C") processor3.execute(event);
...
...
...
...
.
Although there’s nothing wrong with this if/else ladder. But this kind of slows us down. Because whenever there’s a new processor code is being changed at multiple places. On top of that, there’s always a chance that someone like me would copy this code in some place elsewhere in a shared environment 😂 which would be devastating in this case. So, abstraction is good.
Abstraction
Providing abstraction here will shorten the code by a lot and it kind of provides basic plumbing to the entire flow.
class Event {
string type;
EventData data;
}
class Processor {
public:
// pure virtual function providing interface framework.
virtual void execute(Event event);
virtual bool shouldProcess(Event event);
};
Event event;
vector<Processor> processors = {processor1, processor2, processor3, processor4, processor5};
for (auto p: processors)
if (p.shouldProcess(event)) p.execute(event);
I think this plumbing makes it better. And if I would be using Spring/Java then remember autowiring
that would do everything for me. I will just have to write them for a loop.
And don’t confuse this representation with DDD. But you can trust me when I say that context is king and respect the boundaries.
Conclusion
Code redundancy is a good thing.
I want to write so much more on this but I know you won’t read further because it’s already more than 1500 words 😛 So I conclude.
Thanks for reading till the end (if you did). And if you did then make a quick comment and let me know.
Until next time.