"Write a lot of comments in your code" is, by itself, stupid advice. Quantity doesn't help; quality does. Comments are one of many tools in your toolbox that you can use to make your code more readable (or less so, if used wrong) - a chainsaw is a wonderful tool for cutting trees, but you shouldn't use it to trim your toenails.
"Write self-explanatory code" is good advice, but just like extraneous commenting, overly explanatory identifier names reduce readability rather than improve on it. You don't want a method signature that reads
void ExternalWebsiteUser::UpdatePasswordUsingSHA512HashWithSalt(string passwordDoNotPassEmptyStringBecauseItWillCrashTheApplication) throws DatabaseWriteFailedException, PasswordDoesNotMeetPasswordRequirementsException, ExternalWebsiteUserDoesNotExistException.
The real goal to strive for here is readability, and getting this right takes a lot of experience, especially experience reading other people's code, and having other people review yours.
Assume that whoever reads the code:
- is fluent in the programming language and its culture (so don't do
// increment i by 1)
- has access to the documentation of any libraries you use (so don't explain usage of third-party libraries in your code)
- has a rough understanding of the problem domain (so don't jump through hoops describing the entire problem domain)
- is familiar with common algorithms and design patterns (so don't explain how the Factory Pattern works)
Our goal is to clearly communicate both the what and the why of your code. To achieve this goal, we can use the following devices:
- consistent and descriptive, yet concise, identifier naming
- consistent coding style
- idiomatic code (what the Python community calls "pythonic")
- code structure that follows the problem domain
- code that adheres to the Single Responsibility principle
- KISS, DRY, Single Source Of Truth, etc.
From this, it follows that if the other devices are good enough to answer both the "how" and the "why" questions, comments are unnecessary and should be skipped.
Examples of cases where comments are absolutely useful include:
- Hinting at edge cases and potential pitfalls
- Giving an overall description of a complex algorithm or formula used somewhere, maybe hinting at resources describing it in detail
- Defining domain entities in real-world terms when a concise identifier cannot convey the information, even though it has only one responsibility
- Documenting non-obvious side effects
- Documenting non-obvious meanings of return values, parameters, etc.