The pitfalls of re-usable code
I was thinking about, and wanted to purge my mind of, thoughts on code re-usability. It is topic of elegance, and in the wrong hands, nightmares.
I've lately gone from one extreme to another. In my line of work, I'm involved with a very large application that deals with numerous permutations of the same function. However, wherever possible, a single method is handling all of the work. This is a very large and complicated system, and as a result, this one method is enormous. The code for it virtually unreadable despite years of commenting by dozens of coders, and it seems like every bug fix breaks something else. This is where re-use went wrong. The method being reused doesn't solve just one problem. It was designed to solve every permutation of a very complex idea even where one requirement may be odds with another.
And this is rampant elsewhere as well. When I first started working here, I had to learn a language which was completely new to me, xBase. I didn't really have a mentor or anyone actually training me, so I learned the language like a small child. I started with the pieces I understood or that were repeated enough that I could gauge their purpose. In this fashion I learned the basic syntax and operators of the language in the first day. After that I hit a plateau, progress was slow and non-existent for days at a time. To my untrained eye, I just assumed I was too stupid to interpret what was going on. Now, 2 years later, I look back and I'm unsurprised. Even there in that code, which wasn't part of the monolithic method, absurd attempts to reuse code were being taken. In the span of a single line I could go from dealing with one logical path straight to another, only to come back to the original one 20 lines later... all within the same function or state.
I wrote my share of bad code in those first couple of months as I struggled to learn. The easiest way to learn was to mimic. So I too layered additional functionality inside already bloated functions. But I got lucky, about 4-6 months in, I got assigned to probably one of the largest customizations projects we had ever seen. It didn't take long for the full weight of both the bad deigns of others and my copying them to crush me. From that I started down a new path... I would break into a new function when I created new functionality. When my functionality required code that might be reused later I broke it into a new method. I NEVER tried to add functionality to a method to make it do something new.
Re-usable elements of code should be like Lego; Simple pieces that serve a single purpose. You're writing this code to be reused. Even if you know it won't, write it with the intention that it will be reused millions of times. What benefits do you get from this? Well, since you know the code solves exactly 1 problem you shouldn't have to care about how it is being used. Which means, that if you find a bug and fix it, it should automatically improve every other method which calls into it, instead of creating fear that it may break something.
Optimization is simpler; you only have one problem you're solving, so you only have one set of concerns. When you're solving multiple problems at once, you may end up stuck with an inefficient solution because different facets of the different problems you're solving may have different scopes.
Code is more readable. People often disregard the value of this one. But your methods will be shorter and more succinct and thus they will be easier to read, and easier to reuse because you will able to give them more common sense names. For instance if I have a method which calculates the sum of 2 squares I could call it CalculateSumOfSquares and it would be fairly obvious what problem it solves. But if I tried to make a generic method for multiple calculations I couldn't call it that any more, AND it would need to take more parameters and contain more code to sift through. So I may have to call it Calculate, and if I forget what it supports, I may need to rip open the source later to find out if I can indeed use it to calculate the sum of 2 squares. And then going to an even more ridiculous level, I could make it a math compiler, capable of graphing, calculating, generating statistics, and I would need to call it something like Execute... now I have no clue what it does.
My point is, reusing code only works when it is easy to understand and easy to maintain. And to attain that, the methods should solve only one problem.
I've lately gone from one extreme to another. In my line of work, I'm involved with a very large application that deals with numerous permutations of the same function. However, wherever possible, a single method is handling all of the work. This is a very large and complicated system, and as a result, this one method is enormous. The code for it virtually unreadable despite years of commenting by dozens of coders, and it seems like every bug fix breaks something else. This is where re-use went wrong. The method being reused doesn't solve just one problem. It was designed to solve every permutation of a very complex idea even where one requirement may be odds with another.
And this is rampant elsewhere as well. When I first started working here, I had to learn a language which was completely new to me, xBase. I didn't really have a mentor or anyone actually training me, so I learned the language like a small child. I started with the pieces I understood or that were repeated enough that I could gauge their purpose. In this fashion I learned the basic syntax and operators of the language in the first day. After that I hit a plateau, progress was slow and non-existent for days at a time. To my untrained eye, I just assumed I was too stupid to interpret what was going on. Now, 2 years later, I look back and I'm unsurprised. Even there in that code, which wasn't part of the monolithic method, absurd attempts to reuse code were being taken. In the span of a single line I could go from dealing with one logical path straight to another, only to come back to the original one 20 lines later... all within the same function or state.
I wrote my share of bad code in those first couple of months as I struggled to learn. The easiest way to learn was to mimic. So I too layered additional functionality inside already bloated functions. But I got lucky, about 4-6 months in, I got assigned to probably one of the largest customizations projects we had ever seen. It didn't take long for the full weight of both the bad deigns of others and my copying them to crush me. From that I started down a new path... I would break into a new function when I created new functionality. When my functionality required code that might be reused later I broke it into a new method. I NEVER tried to add functionality to a method to make it do something new.
Re-usable elements of code should be like Lego; Simple pieces that serve a single purpose. You're writing this code to be reused. Even if you know it won't, write it with the intention that it will be reused millions of times. What benefits do you get from this? Well, since you know the code solves exactly 1 problem you shouldn't have to care about how it is being used. Which means, that if you find a bug and fix it, it should automatically improve every other method which calls into it, instead of creating fear that it may break something.
Optimization is simpler; you only have one problem you're solving, so you only have one set of concerns. When you're solving multiple problems at once, you may end up stuck with an inefficient solution because different facets of the different problems you're solving may have different scopes.
Code is more readable. People often disregard the value of this one. But your methods will be shorter and more succinct and thus they will be easier to read, and easier to reuse because you will able to give them more common sense names. For instance if I have a method which calculates the sum of 2 squares I could call it CalculateSumOfSquares and it would be fairly obvious what problem it solves. But if I tried to make a generic method for multiple calculations I couldn't call it that any more, AND it would need to take more parameters and contain more code to sift through. So I may have to call it Calculate, and if I forget what it supports, I may need to rip open the source later to find out if I can indeed use it to calculate the sum of 2 squares. And then going to an even more ridiculous level, I could make it a math compiler, capable of graphing, calculating, generating statistics, and I would need to call it something like Execute... now I have no clue what it does.
My point is, reusing code only works when it is easy to understand and easy to maintain. And to attain that, the methods should solve only one problem.
Comments
Post a Comment