The original article is here (medium.com/@bpnorl…
“Clean code should be like well-written prose” – Robert C. Martin
The common problem with bad code is that it has a lot of comments. This is the most obvious sign of messy source code.
The goal of every programmer should be to write clean and expressive code that avoids code comments. The purpose of each variable, function, and class should be implicit in its name and structure.
When other people read your code, they should not read comments to understand what your code is doing. Well-named classes and functions should guide the reader through your code like a well-written novel. When readers see a new class or feature, they should not be confused about what they see in it.
Keep in mind that developers spend less time writing code and much more time reading and understanding code.
Comment masking error
Naming is very important in your code. You should put a lot of effort into naming each piece of code accurately and precisely so that other developers can understand your code.
// Find employees by status
List<Employee> find(Status status) {... }Copy the code
In this example, the name find is not descriptive enough, so the author of the function needs to leave a descriptive comment describing the function’s functionality. When we look at the find function called from another module, its purpose is a mystery. What did it find? What exactly does that mean? Did it return what it found? How do you find what it finds? As Uncle Bob says in his book Clean Code, if you need to write comments, you can’t express your true intentions in Code.
We don’t want to examine the comments above each function to see what it does.
List<Employee> getEmployeesByStatus(Status status) {... }Copy the code
It is now obvious what this function does, which makes comments redundant. Which brings me to the next way to comment poorly.
Redundant annotation
This messes up your code unnecessarily.
// This function sends E-mail
void sendEmail(a) {... }// This function sends E-mail
public class Employee {
...
}
/ **
* @paramTitle The title of the CD *@paramAuthor author of CD *@paramTrack Number of tracks on the CD * /public void addCd(String title, String author, int tracks) {... }Copy the code
In most cases it is forced redundancy. Many companies require this for every feature and category. If your boss asks you to do this, ask them not to.
Wrong level of abstraction
If you have a long feature or need to keep track of what part of the code does, you may be violating these rules:
- Functionality should do one thing.
- Features should be small.
Here’s an example
// This function calculates prices and compares them with sales
// Promotion, check if the price is valid, then
// Send promotional emails to users
public voidDoSomeThings () {// Calculate the price. . .// Will calculate the price and promotional activities into. . .// Check that the calculated price is valid. . .// Send promotional messages to users. . . }Copy the code
When you have successfully encapsulated each part of the logic into a single function, the code should behave just as it does without comments.
Refactoring is as follows:
public voidSendPromotionEmailToUsers () {calculatePrices (); CompareCalculatedPricesWithSalesPromotions (); CheckIfCalculatedPricesAreValid (); SendPromotionEmail (); }Copy the code
Instead of commenting out every part of the code, each logical block should be nicely encapsulated in its own function.
First, it improves readability. Each code block does not have to be read line by line. We can simply read the helper function name and understand what it does. If we want to see more details inside each function, we can look at the implementation.
Second, it improves testability. In the example above, we can unit test each function individually. If you do not wrap these separate function, it is hard to test larger function sendPromotionEmailToUsers each part of the (). Features that perform more than one function are difficult to test.
Finally, it improves refactorability. By encapsulating each part of the logic into its own function, future change maintenance is easier, and functions with separate functions are isolated to change only the behavior of that function. When we use long functions of local variables that persist throughout the function, it is difficult to refactor the function without causing changes elsewhere because of the tight coupling of the function.
Comment out code
Commented out code should be treated as Roadkill. Don’t look at it, don’t smell it, don’t ask where it came from, just get rid of it. The longer you hold it, the longer the rest of the code will smell……
/ *
public voidOldFunction () {noOneRemembersWhyIAmHere (); TryToUnCommentMe (); IWillProbablyCauseABuildFailure (); HAHAHA (); } * /Copy the code
Even if you do not delete others dare not delete. You can always check version control if you need it later, because you must be using VCS, right? (If it weren’t for me)
TODO comment
Do not write TODO comments, not just…… Did you do it? Most of the time these comments are forgotten and may later become irrelevant or incorrect. How will another programmer know if they still need TODO this when they see the TODO annotation later?
Occasionally, though, TODO comments are good if you’re waiting for another teammate to merge (usually not for long). Do this until you can fix it and commit it.
“When you feel the need to write comments, first try to refactor the code so that any comments become redundant.” – Martin Fowler
Explanatory lies
When Jimmy comments a new feature he’s written, he thinks he’s helping any future developer who sees his code. What he’s really doing is setting a trap. His notes can be big lies (no pun intended) lying untouched for months or years, just waiting to become a nasty trap. Then one day, in one of hundreds of refactorings and requirements changes, his comments failed in some distant module, but still misdirected countless refactorers.
When you change a line of code, how do you know that the change won’t invalidate comments elsewhere? There’s no way to know
public class User {...// It contains the user's first and last nameString name; . }Copy the code
Then the requirements changed and they wanted to split the name into firstName and lastName.
public class User {...// It contains the user's first and last nameString firstName; String lastName; . }Copy the code
The comment is now wrong. You can update comments to reflect changes, but do you really want to manually maintain all comments after each change? You are the developer, not the document.
But this comment is easy to notice and has no problem changing. But you can’t be sure that elsewhere in the program, you’ll also comment out the parameter name as the user’s first and last name. Changing a small area of code can invalidate many code comments.
Let’s look at another example:
// Process employees based on status
voidProcessEmployees () {... List < Employee > employees = findEmployees (statusList); . }// This will look for Employees by state listList < Employee > findEmployees (List < String > statusList) {... }Copy the code
Someone is then asked to change the function findEmployees to findEmployees by name list instead of status list.
// Process employees based on status
voidProcessEmployees () {... List < Employee > employees = findEmployees (statusList); . }// This will look for Employees by state listList < Employee > findEmployees (List < String > nameList) {... }Copy the code
First, the annotation findEmployees above is invalid, so it needs to be changed. No problem, right? Wrong.
The comment on processEmployees has also expired and needs to be changed as well. How many other comments have been invalidated by this small refactoring? How many comment lies did this change create in the source code?
Alternatives:
voidProcessEmployees () {... List < Employee > employees = findEmployeesByName (nameList); . } List < Employee > findEmployeesByName (List < Name > nameList) {... }Copy the code
If you name your functions accurately and accurately, you don’t need comments, and you don’t spread lies in your code.
“Code never lies, comments do.” -Ron Jeffries
When do I need comments
I know many developers who are die-hard supporters of code comments, and for them, I have to admit that sometimes comments are ok. But you should have a good reason for each paragraph
Complex expression
If you have complex SQL or regular expression statements, go ahead and write comments. It can be difficult to express such statements cleanly in code. Adding comments to these expressions can help other developers better understand your code.
// The format matches kk: mm: ss EEE, MMM dd, YYy
Pattern timePattern = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w*, \\d*, \\d*");
Copy the code
Comment warning
If you need to warn other developers about possible bugs in this code, leave a comment near the code. These comments can act as harbingers of mysterious behavior in your code and add value to your code.
Intention to clarify
If you do, take responsibility for your inability to write expressive code, and write comments indicating your intent.
If you must write a comment, make sure it is local. Non-local comments far from their references are doomed to fail and turn into lies. Comments referencing functions or variables should be directly above them. A warning comment can be above or next to the code it references. If your IDE supports comment highlighting, make your warning comments stand out from the rest of the code.
The last
I’ve developed a feel for code comments. I despise them, but I know that sometimes they are needed.
So please stop writing so many comments.
This article is shared by the author who saw the discussion of Brian Noland, a foreign god, on Twitter. I hope my code will be as elegant as prose in the future.
supplement
Since the official Twitter account of Java has been retweeted, there has been a very heated discussion on this issue abroad, if you are interested, you can see:
Don’t stop writing code comments
Reddit comments area
Related snippets:
Because it is a translation of the author’s original text, I respect the author’s content and do not modify.
My point is that the code itself needs to be clean, clear, and accurate, but that doesn’t mean all comments are useless, it’s not absolute.
One: We can’t assume that everyone has to pass the English test (even those named in pinyin — such people are forced to write annotations).
Second, the original author is probably against invalid and useless comments in the development process, but does not mention that the release needs to provide an API interface for the use of doc comments. Documentation comments are necessary to facilitate user understanding of the interface. Because the document is bound to the version number, you don’t need to maintain these comments later in development. However, it is likely that many requirements will change before release, unless doc comments are maintained for each requirement change. Otherwise, the resulting directly generated DOC is likely to have errors. The best way to do this is to write doc comments to generate documentation before release.
Third: for the domestic situation, better advice should be: code as elegant and clean as possible, naming standard. In order to take care of other developers, we must write comments if necessary, and attach the time and version number to the comments. When changing the ancestral code, as a successor, we should explain the reason for the change and attach the time as much as possible, and update the comments timely. But minimize redundant comments (that is, the code itself can be expressed clearly, so as not to need to maintain the update later) to avoid misleading. The author of the original article mentioned that every time he wrote a paragraph of notes, he should give himself a reason for the existence of this paragraph of notes.
Four: a lot of people can not understand the reason, because not carefully read, if you are careful enough, you will find the original diss are
// xxxx
Copy the code
Rather than
/**
* xxxx
* @author
* @date* /
Copy the code
OVER