1 introduction
Inconsistent code puts pressure on the team to understand when everyone is on the same team:
- Do the same thing in different ways
- Things that serve a similar purpose have different names
Most programmers understand consistency in the larger sense, for example, should database access be called DAO or Mapper or Repository? Within a team, there should be uniform standards, but at the coding level, the requirements are often less detailed. As a result, we often see code written inconsistently.
2 Different names
Take a look at this code:
As you can see, there seems to be no problem with the current distribution channels. But I was wondering:
- What are WEBSITE and KINDLE_ONLY respectively?
WEBSITE says it will only be published on our own WEBSITE, KINDLE_ONLY says it will only be published on Kindle’s ebook store
- Are they both meant to be released on a separate channel?
Yeah!
- Why not call them XXX or XXX_ONLY?
I guess so.
So the problem is that the names WEBSITE and KINDLE_ONLY don’t match.
Code with similar meanings should have the same name. For example, many teams write business to the service layer, and the various services are called XXXService. When inconsistent names occur, they usually mean different things. For example, business components that are not business entries will have different names that are more consistent with their specific business behavior, such as BookSender, which represents sending work to a translation engine.
In general, the meanings of enumerated values should all have the same business meaning, and when there is a difference, you need to determine exactly where the difference is, which is why it is confusing.
It is clear that the author of this code named the two enumerations only in terms of what they should be called, but ignored the role of the enumerations as a whole.
Fixed code uniform form:
Scheme inconsistency
When a system sends a request to another system, it needs to bring a timestamp. Here, the timestamp is converted into a string type in accordance with a certain format. It is mainly used for transmission, which is convenient for other systems to identify and debug in the development process.
The implementation of the code itself is fine. It even takes into account the multithreading problems of the SimpleDateFormat class itself, so it creates a new SimpleDateFormat object each time.
So why did I say it was wrong? Because this is pre-Java 8, and the version of Java we’re using is post-Java 8.
For a long time, the Java date-time solution has been a controversial design, with many problems ranging from confusing concepts (e.g., Date versus Calendar) to unintuitive interface design (e.g., Date’s setMonth parameter is 0 to 11), and some implementations are problematic (for example, the SimpleDateFormat mentioned earlier requires multi-threaded concurrency and requires building a new object at a time).
This mess has been around for a long Time, and many people have tried to solve it (like Joda Time). Starting with Java 8, the official Java SDK borrowed from various libraries to introduce a new date-time solution. This solution is completely separate from the original solution, which means that all of our work can be handled with this new solution.
Our current project is a brand new one, and we are using Java 11, which means we can use the date-time solution introduced from Java 8. So, our convention in the project is that all the date and time types are going to use this new solution.
Now you probably know what the problem is. In this project, the requirement is to use the new dateformat and Date solution, and here SimpleDateFormat and Date are part of the old solution. So, while the implementation of the code itself is fine, in the project as a whole, it’s a bad taste because it doesn’t keep up with the rest of the code.
Later a new solution was used:
The reason for this is that there are multiple solutions to the same problem in a project. If there is no unified agreement, project members will randomly choose solutions according to their feelings when writing the code, resulting in inconsistent solutions.
Why are there multiple solutions in a single project?
- time
With the passage of time and the development of technology, people will actively realize the problems of the original scheme and propose new solutions. For example, the Java date-time solution here is caused by the evolution of JDK itself over time. Some projects are longer, and similar problems may occur.
- Introduced for its own reasons
For example, introduce libraries in your code that do the same thing. For example, there are Guava and Apache Commons Lang that do the same thing to determine whether a string is empty or a string, so programmers will use one or the other depending on their familiarity, resulting in inconsistent code.
These two libraries are the basis for many libraries, and often dependencies appear in our code as other libraries are introduced. So, we had to agree on what was our standard practice on the project, so that it didn’t go against each other. For example, in my team, we chose Guava as the base library because it is relatively modern, so the team agreed to use Guava for similar operations.
Code inconsistency
Code for creating works in the translation engine:
- First, according to the ID of the works to be processed, the approved works are obtained
- Then, send an HTTP request to create the work in the translation engine
What’s the problem? This code is inconsistent, the code is not a level of code!
The first is to get the approved work, which is a business action, and the next three lines actually do one thing, namely send the request to create the work. These three lines of code:
- Create the parameters of the request
- Create a request based on the parameters
- Finally, send the request
Three lines of code together accomplish one thing, sending a request to create a work, which is a complete business action.
So, the code in this function is not at the same level, either the business action or the details of the business action. With that in mind, extract the code for these business details into a function:
As a result, the original function (createBook) is full of business actions, while the extracted function (createRemoteBook) is full of details of business actions, each statement at the same level.
Distinguish between layers of code, fundamentals, and separation of concerns!
Once you’ve broken down the different concerns, you can further tweak the structure of your code. Methods like the one we split out above, which we already know makes a request to create a work, are not part of the business class per se. So, this part can also be tuned out by introducing a new model:
When it comes to layering, most people think only of layering models; few people think of layering functions. When code at all levels gets mixed up, many problems arise, most typically with excessively long functions.
Again, we’re doing model layering, but this time the starting point is the statements of functions. The meaning of “Separate concerns, smaller is better”. If the granularity of observing code is small enough, many problems will naturally emerge.
When programmers start writing tests, they typically have a problem: how to test a private method. It has been suggested that special abilities, such as reflexes, be tested. My answer to this question is, don’t test private methods. The reason you want to test private methods is because you haven’t done a good job of separating concerns and mixing up different layers of code. It would have been difficult to test the createRemoteBook method, but with the introduction of the TranslationEngine class, the method becomes an open method and can be tested as an open method.
The technical problem that many programmers struggle with is actually a software design problem. Do not solve a problem that should not be solved by ingenious techniques.
conclusion
Consistency is very important for a team and an important way to reduce collective cognitive costs. We see separately:
- Inconsistency in naming
- Inconsistencies in the schema
- Inconsistencies in code.
Code with similar meanings should have similar names. Inconsistent names indicate different meanings and require a valid explanation.
Inconsistencies in schemes:
- Due to the long-term evolution of code
- Libraries that perform the same function exist in the project
Either way, the team needs to agree on a convention to make sure everyone is writing code the same way.
Inconsistencies in code are often the result of different layers of code being written together, typically mixing business-level code with implementation detail code. The way to solve this problem is to extract methods and put different layers of code into different functions, but the premise of all this is still separation of concerns, the underlying code problem is still a design problem.
Make sure your code is consistent at all levels.