How to write code that will be reused

The idea that code designed for reuse can be a panacea for all programming troubles is a dangerous myth. Now I will explain why.

Let's imagine that you are writing a library and suddenly catch on a brilliant idea, from which a generalized solution can be obtained, applicable in a very wide range of cases. You are feverishly prescribing APIs that will overpower any functionality and adapt to any scenario. You add all the scripts that only come to your mind. The code swells and swells, but, ultimately, it really becomes generalized in the fullest sense of the word. He is like hot cakes, and you are happy.

But one day, a new API is emerging on the horizon. It is more concise and suitable for a larger number of scenarios, and moreover, it wins in speed and simplicity. Your API falls into oblivion – everyone immediately switches to a tempting novelty. However, after some time, the same story repeats with it: the API becomes more and more heavy due to constant additions to the code, designed for new conditions, until in the end something else comes to replace it. And so on ad infinitum.

Why is this so?

The root of all problems in this case is the desire to write generalized solutions. If the reproducible code takes a standard case as a standard, it becomes bloated, inconvenient to use, and gradually turns into a pure headache.

Some degree of generalization is a prerequisite for the repeated use of any code. But if you go too far with this, utilitarianism begins to suffer. Therefore, if I express my thought in a nutshell, writing a code that is reusable does not boil down to making it as comprehensive as possible. Everything is much more complicated.
To create a practical reproducible code, you need to aim to ensure that it is not used regularly, but spontaneously, when the need arises. Say, when working on a product, you suddenly come across a code that can be used for its needs. You take this snippet, modify it a little and implement it in the application. Thus, you pay only for what really benefits, and exactly when it is necessary.

Later, you discover that this revised version may come in handy in another application. You again take this fragment, slightly modify it and use it in the third circle. So, by gradually increasing reproducibility, you not only get the maximum benefit from this fragment, but also protect your product from code that does nothing for it and is generally not needed in it.

The main thing here is by no means to try to predict the future. Reproducibility should be limited to current visibility limits; in the future, you will correct the code accordingly whenever a new opportunity arises for its application. This will not only save you time and effort, but also write more economical, cool and modern code, sharpened for reuse.

Here are some practical suggestions on how to make code reusable.

Avoid duplication

As Lemony Sinquet rightly said: “Do not repeat. Firstly, you repeat yourself, secondly, you say the same thing, and thirdly, everyone has already heard that. ”

Remember, we talked about reuse that occurs spontaneously? That is precisely this (and only this) that the purpose of the ergonomic code should be limited. So write the code that you need at this particular moment, and continue in the same spirit until you notice that you have to solve the same problem over and over again. Then do the refactoring, put the code in some accessible location and refer to it as needed. Acting in this way, you get the output not of a wasteful generalized code, but a code in which there is no duplication.

In fact, the DRY principle (Don’t repeat yourself – Don’t repeat yourself) postulates the same thing, which states that the same logic should not be written repeatedly in the code – this gives rise to a technical duty. Meaningless repetitions clog the system, reduce the quality of the code base and, in addition, turn it into a nightmare for those who have the responsibility to maintain the product code. It is important to remember: the DRY principle is a kind of philosophy that calls for abandoning personal programming ambitions and doing what is best for the project. If someone has already done something, use it. No need to reinvent the wheel.

Make sure the class / method / function is responsible for one thing.

On this occasion, we can recall the beautiful statement of Louis Sullivan: "Form comes from the destination." In translation, this means the following: if a function, class or method does one single thing, then you will change them for only one reason. Yes, with this approach you will often have to create methods that will use other methods, but they will remain simple and not too coherent.

Each system is built by means of a subject-oriented language, which was created by programmers with the aim to properly describe such systems. Functions fulfill the role of verbs in these languages, and classes play the role of nouns. Together they, as a rule, form the basic level of organization of any language; accordingly, if you prescribe them in a quality manner, then your code will be of high quality.

There are two golden rules for creating functions and classes that can be applied multiple times, just two:

  • They should be small
  • They should do one thing, and well

This suggests that the function should not grow enough to contain nested structures. The nesting level, therefore, should not exceed one or two. Thanks to this technique, the code becomes easier to read, parse and assimilate.

In addition, it is necessary to ensure that all operators within a single function remain at the same level of abstraction. Mixing levels is always confusing and sooner or later will lead to the fact that the code will be hard to work with. Pro programmers see reproducible code as a kind of narrative, not just a piece of text. They use the capabilities of the selected programming language to create more expressive, meaningful, neat blocks that perfectly build the narrative.

Do not abuse inheritance

Sometimes we, developers, try to look into the distant future of the project and start throwing in additional functions with the thoughts “what if you need it” and “someday come in handy”. Do not do like this. Until now, this has not come in handy for you, now you do not need it and in most cases … you will not need it (to paraphrase the well-known principle of YAGNI – You Ain’t Gonna Need It). This principle applies to inheritance. Do not enter it if you do not have full confidence that the implementation will be repeated multiple times.

With all this, heredity is a great way to add functionality to a class. But programmers tend to grab over the edge, building class hierarchies in six or more levels. The “Gang of Four” in its book “Design Patterns” succinctly describes the risks of excessive inheritance:

"Because the subclass has access to implementation details of the parent class, it is often said that inheritance violates encapsulation."

Heredity leads to a strong cohesion of the components, because the superclass reveals its insides to subclasses, and those, in turn, are completely dependent on the superclass in everything related to the correct operation. In this situation, the superclass loses its flexibility – it becomes difficult to change anything in its functionality. For this very reason, heredity is a bad way to achieve code reuse.

A more reasonable approach is to think in the paradigm of the structure of objects, rather than heredity. This will allow people using your code to more easily grab exactly the functionality they need and create their own objects based on their current limitations. It’s worth even thinking about creating procedural interfaces that each of the classes can implement in its own way. Interfaces are often easier to understand and implement than classes with multi-layer inheritance.

To summarize, it is worth resorting to heredity only if one class is a logical continuation of another and most of the code of the inheriting class uses the code of the superclass. In any other case, you simply sign the death sentence for yourself and the chances of your reuse code.

Finally

In general, writing code for reuse does not mean creating huge generalized monolithic blocks. The key to writing reproducible code is in the elements that are aimed at a specific task, which are easily assembled, soldered well and not too firmly interlinked.

And finally, do not make an end in itself from repeated use – it is not worth it. It is better to set a goal to avoid duplication, not to write empty, unnecessary fragments, to ensure that the code is easy to read and maintain. When you develop the right view of things, reproducibility will come by itself.

Always remember: reuse begins with the fact that you have found a successful solution for a particular task. Everything else should flow from this premise, and you have the right to choose the method that is most appropriate in order to bring a certain piece of code to a new level. As Ralph Johnson rightly remarked: “Before you think about reusing code, you need to make sure that you can use it at all.”

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *