Premature Optimization is a phrase that is often heard in software engineering. It’s where you envision some potential performance bottleneck, so as you are writing the code, you go out of your way to create the most optimum code (ie written in a way that the compiler can execute as fast as possible). In the process you create code that is incredibly hard for humans to read and understand. The kicker is that part of the code may not have even been the actual bottleneck.
In fact, there may not even be a bottlekneck. The performance of a simpler algorithm may have even been perfectly adequate. That’s why software engineers caution against premature optimization. When you first start out you don’t know what to optimize and you don’t even know if you need to optimize. You end up making your code overly complicated for perhaps no real benefit. Therefore, the traditional advice is: Make it work, then figure out if there is a bottlekneck and identify it and then optimize just that part. There’s a very similar topic that doesn’t get quite as much attention and that is premature reuse.
What do I mean by code reuse?
Let me preface this discussion by defining what I mean by code reuse. By code reuse, I mean taking a module of code and packaging it in some way (VIPM, git submodules, PPL) so that you can use the same piece of code in multiple projects. Write once, use many places.
Why reuse code?
The argument goes like this: If you’ve written something and it is tested and works, why reinvent the wheel? Why not just package it up and now you can go use it in future projects. It will save you a bunch of time because you won’t have to rewrite the same functionality. And it should make your code more robust because you’ve already worked all the bugs out. It also helps standardize your code – every project does things the same way. There’s a lot to like about reusing code.
Shouldn’t everything be reusable?
There are certain schools of thought within software engineering that put a very high value on reusability. In their minds, almost everything should be reusable. When they write a new piece of code, the first thing they think about, often before they even start writing the code, is how they are going to reuse it. I disagree with that approach. I view it as a form of premature optimization.
Now don’t get me wrong, I’m not against reusability. I do reuse some code, but it’s a small portion of my code and usually highly targeted. I reuse a lot of driver level code and I also reuse a lot of tools, but there is a lot of application logic in my code that is not reusable and I’m ok with that. What I am against is putting in the effort to make code reusable when it hasn’t been proven necessary yet.
What is the downside to reuse?
There is overhead in managing reuse code. Implied in reusing code is that you have some way of versioning that library and managing the versions. You also need a process for updating the reuse library. What to do when you find a bug? Okay so you go and edit your source and rebuild your reusable library and use the new version going forward. But what about old projects? Do you go back and update those? Probably not, unless you have some other reason to revisit them.
Another problem is that designing good reusable code is actually fairly hard. Often times you’ll get a project that has some features that are similar to features from a previous project, but just a little different. So you add a few extra inputs to the API and that works. But over time those APIs get bloated and your reuse code becomes a swiss army knife that tries to do everything, but does it all poorly. Now there are ways to avoid that and to use OOP patterns and other techniques to write good extensible APIs, but that takes a lot of effort. It also makes that code harder to reuse, because it is not as not simple as just dropping in the new code. You have to create child classes specific to your implementation.
Another problem is that you have to have some way to store and distribute reuse code and let other people know that it is available. What good is a reuse library if no one knows about it and they all reinvent the wheel anyway? VIPM helps with this, but even though it exists people still go around reinventing the wheel. Also keep in mind that just because you have a reuse library doesn’t necessarily mean you will actually have an opportunity to reuse that code in the future. I have a lot of reuse libraries that I thought I would reuse that I haven’t used or touched in years. Some of them I’ve never actually reused.
After publishing this, Steve Watts pointed out another problem with reuse and that is blind faith. That is people grab a reuse library and use it without actually fully understanding what it does. One might argue that a good API doesn’t require a deep level of understanding of what is going on underneath. However APIs are abstractions and all abstractions leak. You at least need to understand where the leaks are and how to plug them.
I read about this idea somewhere and I really can’t remember where, so I apologize for not giving someone else credit. The idea is the phrase “1,2,n” and it means: The first time you do something, don’t worry at all about making it reusable, just make it work. The second time you need to do the same thing, still don’t worry about making it reusable, just copy and paste it and modify it if you need to. The 3rd time, that is when you should take the time to make it reusable n times.
Why 3 times in the 1,2,n rule? What is special about the 3rd time? When you first write a piece of code, you have no idea if it is ever going to be reused or not. Just like you have no idea if it needs optimized or not. Just like avoiding premature optimization, we should avoid making the code reusable until we actually have a chance to reuse it.
Okay, so why not create a reusable library the second time? That is a valid question. The answer is because we don’t know enough yet about the different contexts in which it will be used. We don’t know what inputs and outputs we’ll need on our API yet. At that point we’d just be guessing.
When we go to use it the 3rd time, we’ve got 3 contexts to reference when we design our API. At that point we can do a pretty good job of designing the necessary flexibility into our API without turning it into a swiss army knife.
What about templates?
In all this talk of reuse there is another concept we often overlook and that is templates. Yes, it can be a little bit of copy and paste and some people look down on that. But you can also make it more formal. You can have LV project Templates and DQMH module templates. The benefit to templates is that you can easily adapt the code to your specific situation, as opposed to reuse code which can sometimes be rigid.
The argument I hear against templates is “Well, what if I find a bug in my template? Everywhere I used that template previously will have that same bug and its going to be hard to find them all and update them”. To that argument, I would say “How often do you find and fix a bug in your reuse libraries and then take the time to go find everywhere you used that library and update it?” It’s the same problem. Sure once you find all the instances, it is a little bit easier with things like VIPM, but most people don’t go update old projects anyway.
Don’t misunderstand the point of my post. I do like reusing code and I get lots of benefit from it, but not everything needs to be reusable just like not everything needs to be optimized. Just like we avoid premature optimization, we should also avoid premature reuse. Doing work that never needed to be done in the first place is a waste of time. Add to that you have to maintain every line of code you write. So let’s be conscious of how we spend our time and spend it on the things that will have the biggest impact.
If you need help making your organization more efficient by utilizing code reuse and templating and coming up with a strategy that works for you, let’s talk. Use the button below to schedule a call.