Why Code Abstraction Isn’t Always Good? (And What You Should Do Instead)
Little things you learn everyday with hands-on experience

Being an engineer isn’t an easy job, it’s one of those jobs that require you to exercise your brain and make decisions.
And as we all know making decisions in itself is tiring and most times decisions will lead to consequences. If it’s a simple decision where the consequence is black or white whereby you know certain decisions will lead to bad consequences, you intuitively avoid it and go for the safer approach.
But what if your decision isn’t too bad?
What if your decision comes down to tricky scenarios such as:
Both decisions end up having benefits but at a different scale
Both decisions have benefits and consequences that are not easily comparable to each other
At times, solutions might have worked very well for the industry that you were in but in other times, they don’t do so well in others even though the solutions are the exact same.
This is a realization that I’m starting to have as I gain more experience while taking up bigger projects in my company. As a junior engineer climbing up to a mid-level engineer, I’m starting to realize how theory learned in school is simply theory and the application in a real-life context is a different ball game.
But it’s fun and interesting in its way.
What Theory Teaches Us?
If you’ve ever studied Object Oriented Programming (OOP), you have come across terms such as Inheritance and Polymorphism.
Supposedly, they help you to write readable and clean code that is extensible and easy to read so that the engineers that you’re working with don’t have to spend too much time and energy to understand your code. This will help them with editing your code and extending them if required.
According to GeeksForGeeks:
Inheritance is one in which a new class is created that inherits the properties of the already exist class. It supports the concept of code reusability and reduces the length of the code in object-oriented programming.
The code looks something like this:
class A { }
class B extends A { }Polymorphism:
Polymorphism is that in which we can perform a task in multiple forms or ways. It is applied to the functions or methods. Polymorphism allows the object to decide which form of the function to implement at compile-time as well as run-time.
class A {
public void add(int x, int y)
public void add(int x, int y, int z)
}
class B extends A {
public static void main(String[] args) {
A a1 = new A();
// method overloading (Compile-time polymorphism)
a1.add(6, 5);
// method overloading (Compile-time polymorphism)
a1.add(1, 2, 3);
B b1 = new B();
// Method overriding (Run-time polymorphism)
b1.print();
}
}And when we tie in this theory with the idea of abstraction, code duplication decreases and readability supposedly decreases if you have multiple files with similar behavior. When you need to add another similar file, all you need to do is change the behavior for each sub-class and you are done.
This is what I was taught in university.
My Encounter With Abstraction
When I applied this to my job in this particular project, I realized this wasn’t necessarily the most optimal scenario to apply it.
When I first got the ticket to create multiple similar classes with slightly different behavior, I thought of writing the classes using abstract classes since it would help to reduce repetitive code and duplicate logic.
Along the way, one of my senior engineers also told me to apply abstraction here in a similar way as I thought it should be but when I did that, I couldn’t help but feel like some parts were forcefully abstracted even though logically everything was sound.
It turns out I’ve taken the idea of abstraction too far.
The code became harder to review even for engineers who knew the project inside out. There wasn’t a need to apply abstraction to all of those classes because some of those classes might be easily extendable in the future. It made adding another one of those subclasses much easier but it reduces the clarity for those engineers since they would need to step into multiple files to thoroughly understand the logic of the code.
This was a clear example of the idea that theory doesn’t always apply to real-world contexts.
My Learnings
Applying what you’ve learned in school might seem sound since it might seem to be more efficient that way. But the whole point of writing code is to convey intention. And to do that, we have to write code that is clear and concise for other engineers to read them.
In the real world, it’s no longer about writing code that can work.
It’s much more than that and every decision you make when writing code has a certain consequence. So you will need to weigh your options, identify tradeoffs within your team, and decide on the most optimal solution.
But of course, learning how to make a decision comes from forming opinions, and opinions come from having a deep understanding of a certain topic, being able to see things from different points of view and finally deciding on a solution.
Tieing Things Up
Teachings in school can only bring you this far.
The rest comes down to the uncertainties you face in the real world, the inconsistencies in different work industries or projects, and the complicated decision-making process you need to make as a software engineer every day.
But that’s what makes the job of a software engineer fun.
It’s to fix problems with well-thought-out solutions.
Want to know about my Software Engineering Journey?
I’m Maguire, a software engineer working in Wise, building awesome products. I write about engineering and fitness. If you’re interested, join my newsletter to receive learn more about my experience software engineering journey!
🤓 Check out my other social links here.

