In software development, we have all learned some classic design principles, including the object-oriented principle (SOLID), the simple principle (KISS), the single principle (DRY), the least principle (LoD), the separation principle (SoC), etc.

When you look at these principles in isolation, they all make sense and seem like they can improve your programming and code quality if you just follow them, but why is it that when it comes to actually coding, you feel ambivalent about making choices?

In fact, the reason lies in the fact that some principles conflict with each other and some principles overlap with each other. Although design principles are intended to guide programming practices, if they are not limited in scope, they can easily be misused or abused and lead to more problems.

In this article, I’ll start with the DRY principle.

DRY principle

DRY (Don’t Repeat Yourself) translates to “Don’t Repeat Yourself” in Chinese.

This principle first appeared in the classic book “The Way of Programmer Training”, defined as follows: each function of the system should have a unique implementation, if the same problem repeatedly encountered, should abstract out a common solution, rather than repeatedly develop the same function code.

The principle is simple and clear. Almost everyone can accept this principle, and it is often used in programming practice. For example, if multiple pages of a Web backend management system need to query basic personnel information data, then you can abstract a general personnel query function module to solve this problem.

But it’s easy to fall into a few mental traps when using DRY principles to guide programming. What are some of them? How do you avoid these pitfalls?

Pitfall # 1: Always care about code reuse

As a programmer, you’re often involved in developing code for different functions, some of which have some commonality, and some of which have some duplication, so it’s easy to think of the DRY principle, because no one wants to do a lot of duplication. You might do something like this:

  • Always be aware of duplicate code;

  • Always care if your code is reusable;

  • Always be aware of wasting time because of repetition.

But after a period of time, you will find that, although the above approach will make the code more clear, also can to a certain extent, improve code reuse, but some abstract code is not expected that high versatility, and may even project in the short term is over, the back of the project haven’t used at the “reuse” code.

This is a common problem for programmers, and the nature of the problem is that you may have neglected to think about whether your code really needs to be reused.

There are three common types of code duplication:

  • Repeated functional requirements, for example, team A and team B have done online document management function, QQ and wechat chat communication function, etc.

  • The implementation logic is repeated. For example, for the same file uploading function, student A uses the Spring framework to achieve it, while student B uses JDK native functions to achieve it.

  • The invocation is repeated. For example, the user password verification is invoked before the user information is queried on the login page, and the user password verification is invoked when the user information is queried.

Do you immediately want to optimize when you see repetition? For example, if you are A member of team A, you want to convince team B not to maintain online documentation. You are student B, and you think student A is just simple encapsulation with poor scalability and other problems.

Tell me about a problem I had with the company. A colleague abstracted a good method and I called it directly, only to find that I needed to modify a lot of things myself. Very troublesome. Instead of simplifying, this abstraction became complicated, and I ended up writing one myself from beginning to end. Believe that others have had the same problem as ME.

You see, it’s easy to get so focused on repetition that you forget to think about whether code repetition is really a problem.

So how do you avoid them? Six simple words: Use first, reuse later.

Sometimes your code repeats because you haven’t found the right abstraction, and you shouldn’t force it.

Traditional theory says that code repeated more than three times should be abstracted. This is wrong! Repeating the code three times or more is not necessarily related to finding the correct abstraction. In other words, don’t be fooled by the familiarity of a moment of programming. Write the current code realistically and compare it to see if it’s really repetitive.

In short, you should write code that works first and then reuse it.

If you haven’t found the abstraction yet, it doesn’t really matter, because you can refactor it when you have more context.

So, to avoid repeating yourself, you need to stop worrying about code reuse all the time, reserve the right amount of repetition, and then abstract reusable common code when it does repeat.

Trap 2: Overdesign

In recent years, with the gradual popularity of object-oriented thought, in software development, we pay more and more attention to the extensibility and maintainability of the code, so the development team began to emphasize more and more flexible code design, the purpose is to deal with more changes in the future requirements.

But in fact, too often, the emphasis on flexibility and reusability makes it easy to overdesign code.

For example, the user puts forward the requirements of A, and the designer analyzes the system requirements of B and C to support A. However, in the process of coding and implementing B, it is found that better general functions D, E and F can be abstracted to support B and C, and then D, E and F can be developed. You start to scatter your energies, and instead of just developing B and C, you have D, E, and F.

This happens because we expect flexible design now to avoid repetitive design and coding when requirements change in the future. Doing so is really in line with the DRY principle. But in practice, the direction of demand is unpredictable. Once we put too much effort into flexible design, we will inevitably affect the requirements that should be fulfilled. At the same time, too many features introduce more potential problems, and fixing them takes time and effort.

So how to avoid the pit? One way: Take context and design appropriately. When designing code, you should follow the principle of limited scope, which is often referred to as grasping context. , for example, you need to develop an internal data management background rights management function, so you should seize the context is the data is sensitive, use the approximate scope of and number, feature delivery deadline, team did now use the class library and framework, using basic access authentication, etc., and then began to design and code implementation.

Once you understand the basic context, you can begin to design and code appropriately. While there is no universal standard for moderation, I recommend a simple and practical approach: When you want to extend the universal design, think about whether the project will still exist a year from now.

It’s a cliche but it works because I’ve killed a lot of my own overkill over the years, so I encourage you to try it too. Of course, there are some very basic general-purpose tool methods that can be abstracted and reused, but you can still use this method to check, and you will find that many functions are only used once.

Trap 3: Write one-time code

If we understand the DRY principle dogmatically, it’s easy to go to the extreme of not repeating for the sake of not repeating, commonly known as writing disposable code. In fact, hardcoding and copy-and-paste programming, two classic coding methods, are examples of this scenario.

  • Hard coding refers to writing configuration data or general information into code so that if the information changes, the code has to be modified to meet the requirements. For example, if a user name, password, and email address from an email sender are written into the code, when the password changes, the maintainer needs to modify the code and recompile the package.

  • Copy-and-paste programming is the practice of copying code that someone else has already implemented into your own code to achieve the same functionality. This is one of the most common ways of programming today, and is popular not only with novice programmers, but also with many skilled programmers, for the reason that they don’t reinvent the wheel.

You’ve probably already found that writing one-time code is a good fit for the DRY principle, because it not only avoids duplication (you write new code every time), but also improves productivity in the short term (you get things done on time).

However, in my opinion, this is just a superficial non-repetition. Writing one-off code will not only lead to frequent changes and more bugs, but also cause the architecture to fail to evolve in a timely manner and accumulate more technical debt, which ultimately leads to a huge and unintelligible system.

So how do you avoid this pit? Always write code that is easy to understand.

What is understandable code? In my opinion, easy-to-understand code conforms to the following characteristics: the code logic is clear, make full use of language features, comply with certain coding specifications, and achieve complete requirements.

Let me explain it in detail.

First, understandable code does not mean easy, simple code. Simple code is often harder to understand, and precisely because there is less code, it is more expensive to learn.

Second, easy-to-understand code can take advantage of language features. For example, in Java, camel case is used to distinguish the naming meaning of different methods, or familiar conventions such as GET, set, INSERT, update, or special annotations such as @service are used to identify services.

Third, easy-to-understand code needs to follow certain code specifications. For example, interface definitions are annotated, the MySQL database uses underscores to separate field names and note meanings, uses enumerations to define status values, and nested up to three levels of if-else.

Fourth, understandable code should run correctly. Don’t write a lot of documentation and comments to make it easy to understand, but forget how the code works properly. For example, when implementing an interface, you write a comment but forget to implement the code logic (only return NULL), resulting in no result after running; When the input type or position of a function method changes, it does not modify the description, but misleads the maintenance personnel to enter incorrect data, resulting in errors in the result.

Fifth, always remember that understandable code is not an answer to what you tell the computer to do, but an intention to tell another programmer what you want the computer to do.

Simply put, code is written to be read by others, and code that is easy to understand is code that is easy to maintain. If your code can’t figure things out quickly enough on its own, chances are it won’t be readable either.