Six things I learned from writing code in Ali

8.14 header map.png

After writing code for many years, I always feel that how to write clean and elegant code is not an easy task. According to the theorem of 10,000 hours of intentional training, assuming 8 hours a day, 20 days a month, 12 months a year, it takes about 5 years to become a master. In fact, it is impossible for us to actually spend 8 hours writing code in our daily work, and most of the time we are completing tasks. When the business pressure is very high, the goal we may want to achieve is how to make the function work as soon as possible. , whether the code is clean and elegant is very likely not to be placed on the first priority, but how fast it comes.

In this case, it is very easy to owe technical debt. After a long time, such code is basically unmaintainable, and it can only be rebuilt. This cost is very high. It is only a matter of time before the debt is repaid, and when it is repaid, there will be a lot of extra interest. It may be yourself or the successor who will pay off the debt, but it is the team that is paying off the debt. So from the team's point of view, writing good code is a very necessary thing. How to write clean and elegant code is a very difficult subject. I have not found a universal solution, but more of a trade off, which can be discussed a little bit.

Is the code written for humans to read or for machines to read?

In most cases I would think that code is written for people to see. Although the final executor of the code is the machine, in fact the code is more often seen by people. Let's take a look at the life cycle of a piece of code: development --> unit testing --> Code Review --> functional testing --> performance testing --> online --> operation and maintenance, bug fixes --> test online -- > Retire offline. The time from development to launch may be several weeks or months, but the cycle of online operation and maintenance and bug fixes can be several years.

In the past few years, it is almost impossible for the original author to maintain it. How the successor can understand the previous code logic is extremely critical. If it cannot be maintained, it can only be re-created by itself. So what we often see in the project is that when we see the code of the predecessor, we all feel that this is garbage, and the writing is messy, so I should rewrite it myself. Even in the process of development, others need to come to Code Review. If they can't understand the code, how can they do the Review? And you also don't want to call you for help when you are on vacation because other people don't understand your code. I was very impressed by this. I remember that when I was working not long ago, I went back to my hometown for a vacation. Suddenly a colleague called and asked me how to solve a problem. , but I still have to support until my phone bill is exhausted.

Therefore , the code is mainly written for people to see, and it is the way for us to communicate . Although those very good open source projects have documents, we still look at their source code more. If the code in the open source project is hard to read, the project will basically not be popular. Because code is the basic way for us developers to communicate, and maybe even verbally discuss things that are not clear, we can make it clear through code. The readability of the code I think comes first. Each company is estimated to have its own code specifications, and following the relevant specifications to maintain a unified code style is the first step (recommended Google code specifications and Microsoft code specifications). The specification generally includes how to name variables, classes, and functions, the functions should be as short as possible and atomic, not to do multiple things, the basic design principles of classes, and so on. Another suggestion is to learn more about the code in open source projects.

KISS (Keep it simple and stupid)

The general working memory capacity of the brain is 5-9. If there are too many or too complicated things, it is impossible for most people to directly understand and process them. Usually we need some auxiliary means to deal with complex problems, such as taking notes and drawing, which is a bit similar to borrowing external memory when the memory is not enough.

Students who learn CS know that the access speed of external memory is definitely not as fast as memory access speed. In addition, in general, the possibility of errors in complex logic is much greater than in simple cases. In complex cases, there may be many branches of the code. Whether we can consider each situation in place, these are difficult . In order to make the code more reliable and easy to understand, the best way is to keep the code simple, try to use simple logic when dealing with a problem, and don't have too many variables.

But real problems are not always so simple, so how to deal with complex problems? Rather than borrowing external memory, I prefer layered abstractions to complex problems. Network communication is a very complex thing, and there can be countless devices used in the middle (mobile phones, various IOT devices, desktops, laptops, routers, switches...), the OSI protocol abstracts each layer, each The situations that the layer needs to handle are greatly simplified. By decomposing and abstracting complex problems, the problems we need to solve at each level are simplified. In fact, it is also similar to the divide-and-conquer in the algorithm. For complex problems, we must first dismantle them and turn them into small problems, so as to simplify the solution.

KISS has another meaning, "Do not multiply entities unless necessary" (Occam's Razor). There is a sentence in CS "All problems in computer science can be solved by another level of indirection". In order to expand the system and support some possible changes in the future, we often introduce a layer of indirection or add an intermediate interface. When making these decisions, we need to think more about whether it is really necessary. Adding an extra layer gives us the benefit of being easy to scale, but it also adds complexity and makes the system more incomprehensible. For the code, it is likely that I am calling an API here, and I do not know where the actual trigger is, which may increase the difficulty for understanding and debugging.

KISS itself is a trade off. It is necessary to simplify complex issues through abstraction and splitting, but whether it is necessary to do more abstraction of indirection in order to preserve changes, these need to be carefully considered.

DRY (Don't repeat yourself)

In order to quickly implement a function, knowing that there is a similar one before, copying the code and modifying it may be the fastest method. But copy code is often the source of many problems and bugs. One type of problem is that the copied code contains some other logic, which may not be needed in this part, so there may be redundancy or even some additional risks.

Another type of problem is that during maintenance, we don't actually know how many other places need to be repaired after repairing one place. This problem has occurred in my past projects. One problem has been fixed before. A few days later, another customer raised a similar problem on another path. The same logic should only appear in one place as much as possible, so that when there is a problem, it can be fixed at one time. This is also an abstraction. For the same logic, abstract it into a class or a function, which is also conducive to the readability of the code.

whether to write a note

Personal opinion is that most of the code should not be commented. Code itself is a language of communication, and programming languages ​​in general are more precise than the spoken language we use every day. With good naming conventions while keeping the logic of the code simple, the code itself is clear and probably reads as a good article. Especially in OO language, the addition of object (noun) and operation (generally used verb) can already explain what it is doing. It is repeated that putting the name of this operation in a comment does not increase the readability of the code. And in the follow-up maintenance, there will be cases where the code is modified, but the comments are not modified. I've seen this happen in a lot of code reviews I've done. Try to write the code as understandable, not through comments.

Of course, I'm not against all comments. Comments are required on the public API. The pre- and post-conditions of the API should be listed, explaining how to use the API, which can also be used for the documentation of the automatic production API. It is necessary to add explanations of these logics and algorithms to the places where some special optimization logics and algorithms are responsible.

Do it right once, don't trust Refactoring later

Generally speaking, write TODO in the code and wait for refactoring or improvement in the future. Basically, there will be no future. We can go to our code base and search for TODOs to see how many there are and how many years ago. I believe this result will surprise you (you are welcome to leave a message to share the results after your search).

Try to do it right the first time, and don't believe that you will come back to refactor the code in the future. People are lazy. Once the current thing is completed, the probability of returning to deal with these after move on is very small, unless the code needs to be modified next time. If you say you won't come back, then this TODO is meaningless. If you really need to, don't leave this question. I have seen some people leave a TODO, throw a not implemented exception, and then a few days later, other students brought this code online, and they died directly. Try not to TODO, do it right at once.

Want to write unit tests?

Personal opinion is a must, unless you're just doing prototyping or quickly iterating over throwaway code.

Unit tests are typically automated tests written and run by software developers to ensure that a section of an application (known as the "unit") meets its design and behaves as intended. In procedural programming, a unit could be an entire module, but it is more commonly an individual function or procedure. In object-oriented programming, a unit is often an entire interface, such as a class, but could be an individual method. 

 From Wikipedia

Unit testing is to ensure that the code we write is indeed the logic we want to express. When our code is integrated into a large project, subsequent integration tests, functional tests and even e2e tests cannot cover every line of code. If the unit test is not done enough, it will actually leave some black holes in the code that you don't know about. One day the caller changes something, and it may hang up when it reaches a branch that is not commonly used. A similar situation has occurred in the project I brought before. The code has been online for several years. Once, I slightly changed the parameters of the caller. I thought it was a small change, but it hung up when I went online, because there was no one before I encountered it. Tested branch. Unit testing is to ensure that the code we write is implemented according to the logic we want, and we need to try to achieve a relatively high coverage to ensure that there are no black holes left in our own code. Regarding testing, I want to open a separate discussion, so I will briefly talk about it here.

It is indeed very difficult to write good code, and you need to consider correctness, readability, robustness, testability, scalability, portability, and performance. The previous discussions are just some of the points that I think are more important to get started. If you want to write good code, you need to deliberately consider and practice before you can really achieve your goals!

The First Cloud Native Microservices Conference

The PC-side address of the First Cloud Native Microservices Conference: https://developer.aliyun.com/topic/microservices2020#/

" Alibaba Cloud Native focuses on microservices, serverless, containers, Service Mesh and other technical fields, focuses on cloud native popular technology trends, and cloud native large-scale implementation practices, and is the official account of cloud native developers that understands the most."

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324124472&siteId=291194637