Code should be self-documenting, there’s no need for comments.
As a developer, you’ll have heard someone in your team say this phrase at one point if not many. It’s a common ideology many repeat, often because they think it’s the right viewpoint to have as a developer and not necessarily what they believe in themselves.
What’s My Opinion?
I feel that comments have a time and a place, especially with different programming languages. Don't overuse comments. I utilise comments when the code is not self-explanatory. Some systems I've worked on in the past can be convoluted or have some "weird" code in there for very specific situations.
I agree that function/method names should be self-explanatory, and developers should be able to read the code and know roughly what is done just by skimming through.
Complex Algorithms
When writing extension methods/functions in code that can use algorithms that others in the team may not know, or truly understand, comments can be extremely useful in explaining the inner workings.
The “self-documenters” would argue the code should be written in a way that would explain its intent without the comments, however, I strongly believe the comments explain the unusual algorithm to any current or future developer.
It can also explain the logic behind it. In the example below I’ve used comments to explain the all-important if
statement to prevent a never-ending loop during recursion. We can also use it to explain the spreading / recursive syntax within the return statement, something without comments may not be obvious to some developers.
const quicksort = (arr: number[]): number[] => {
/* Base case: arrays with 0 or 1 element are already sorted,
return to avoid never ending loop */
if (arr.length <= 1) {
return arr;
}
const pivot = arr[Math.floor(arr.length / 2)]; // Choose the middle element as the pivot
const left = arr.filter(x => x < pivot); // Elements less than the pivot
const middle = arr.filter(x => x === pivot); // Elements equal to the pivot
const right = arr.filter(x => x > pivot); // Elements greater than the pivot
// Recursively sort the left and right sub-arrays, then concatenate the results
return [...quicksort(left), ...middle, ...quicksort(right)];
};
Non-Obvious Workarounds or Hacks
Code often includes workarounds for bugs in third-party libraries or other non-obvious solutions. It's helpful to add comments to explain the changes / temp fixes.
Comments also enhance Intellisense with additional information, using formats like JSDoc (JS), TSDoc (TS), or comment XML notation (C#). The example below albeit verbose is merely for illustration purposes.
Example:
/**
Parses a date string in the format 'YYYY-MM-DD' to a Date object.
This workaround ensures that the date is correctly parsed regardless of the user's time zone. Were seeing issues with JS Date usage across multiple timezones.
@param dateString - The date string in 'YYYY-MM-DD' format.
* @returns A Date object representing the given date at 00:00 UTC.
*/
const parseToUtcDate = (dateString: string): Date => {
const [year, month, day] = dateString.split('-').map(Number);
// Workaround to ensure the date is parsed correctly in UTC
const date = new Date(Date.UTC(year, month - 1, day));
// Validate the parsed date
if (isNaN(date.getTime())) {
throw new Error('Invalid date format. Expected format: YYYY-MM-DD.');
}
return date;
};
Clarify Intent or Business Logic
const applyDiscount = (user: { isPremiumMember: boolean; discount: number }): void => {
if (user.isPremiumMember) {
user.discount = 0.2; // Premium members get a 20% discount
} else {
user.discount = 0.0; // Non-premium members get no discount
}
}
Sometimes we have to implement temporary fixes to solve a problem, however to people unbeknownst of the problem, without comments they might be wondering why we’ve made a business logic decision, and the use of comments adds clarity.
const calculateTotal = (cart: Item[]): number => {
//Temp Hotifx: Use 0 as the default price for items with undefined prices
return cart.reduce((total, item) => total + (item.price ?? 0), 0);
}
Bad Examples of Using Comments:
Redundant Comments: Adding comments that just aren’t needed. This is a perfectly good example of self-documenting code, there is no need for comments.
const addNumbers = (a: number, b: number): number => {
// Add a to b
return a + b;
}
Misleading Comments: Adding comments which give misleading information
const multiplyNumbers = (a:number, b:number)=>{
//Add the the two numbers
return a * b;
}
Overly Verbose Comments: There is such a thing as writing too much in your comments. You should aim to keep your comments direct, succinct and as small as possible.
/**
* This function takes two arguments, a and b, both of which are numbers. It then
* multiplies these two numbers using the multiplication operator (*) and returns
* the result of the multiplication, which is also a number. Multiplication is a
* basic arithmetic operation that combines two numbers into a single product.
* For example, if a is 2 and b is 3, the result will be 6.
*/
function multiply(a: number, b: number): number {
return a * b;
}
Useless Comments:
Useless comments provide no meaningful information, sometimes stating the obvious or including trivial notes.
let count = 0; // Set count to 0
function increment() {
count++; // Increment count by 1
}
Commented-Out Code Without Explanation
Leaving commented-out code without any explanation as to why it's there can confuse future developers.
const calculateArea = (radius: number): number => {
// const pi = 3.14;
const pi = Math.PI;
return pi * radius * radius;
}
Unmaintained Comments
Unmaintained comments that no longer reflect the current state of the code can be misleading.
// This function fetches user data from the server
const getUserData = () => {
// Fetch data from local storage
const data = localStorage.getItem('userData');
return data ? JSON.parse(data) : null;
}
Personal Notes, Opinions & Bad Language
Including personal notes or opinions in comments is unprofessional and unhelpful.
// I tried so many f***g times to get this to work, this was the
// best I could do.
const computeFactorial = (n: number): number => {
// This is stupid, but it works...
if (n <= 1) return 1;
return n * computeFactorial(n - 1);
}
Bad commenting practices can make code harder to understand, maintain, and secure. Comments should be meaningful, concise, and accurately reflect the code's purpose and behaviour. They should add value by explaining the "why" behind the code, rather than restating the "what" that is already clear from the code itself.
Conclusion
I am a strong advocate for the strategic use of comments in modern code. Comments play a critical role in providing context and clarifying complex logic that might not be immediately obvious. Without any comments, the next developer might struggle to understand decisions such as why a database is queried multiple times, potentially leading to confusion or even unnecessary code changes.
Here are some best practices for commenting in code:
- Use Descriptive Naming: Choose well-named variables, methods, and classes to minimize the need for comments. Clear names often reduce the need for additional explanations.
- Write Clear Code: Ensure your code is logical and well-structured. Good code design should make it easy for developers to understand the purpose and flow based on variable names and return types.
- Provide Purposeful Comments: Use brief comments to explain the purpose and rationale behind complex or non-obvious code. Focus on the "why" rather than the "what."
- Avoid Over-Commenting: Do not comment on everything just for the sake of commenting. Focus on areas where comments add real value.
- Skip Humor: Avoid using humorous comments in your code. While they might be entertaining, they are unprofessional and take up valuable space.
Not every line of code, function, or class needs a comment. Use your judgment to determine where comments are most beneficial for you and your team. If a comment helps clarify your thought process or the code's intent, others will likely benefit from it as well.
While striving for self-documenting code is a good practice, comments are essential for providing context, explaining complex logic, and documenting workarounds. They bridge the gap between the code's functionality and its purpose, facilitating easier understanding, maintenance, and extension of the codebase.
Top comments (18)
I honestly don't see any difference between these two. They both simply spell out the code in prose without adding any additional information.
The only difference I see is that the first bit of code seems like the user discount should be extracted into a configuration source rather than hard-coded like that.
Meanwhile, an example like this:
Is ironically a situation where I would use a comment, but the example once again just spells out the code.
I read the code and notice 0 is used as the fallback price. I wonder how that could make sense and read the comment for more context only to find... what I already knew.
A better comment would have been:
Now I read the code and know that it makes sense and isn't an error, and that somebody (maybe even past me) has already confirmed those things. It also gives me a lot more context regarding whether it might be time to remove this hotfix or it is still necessary: Since it was a one-off import error, I can check the database for NULL prices and if they've been fixed, remove the code.
And lastly:
Nah, a bit of humour isn't unprofessional. Just avoid jokes that you wouldn't be comfortable telling to your coworkers or a stranger directly, and don't overdo it.
Take your comments on board with the hotfix, however it’s a lengthy comment , the one used merely as an example purposes was to highlight it can be done and has a brief explanation.
Commit message could have more detail.
I'm not generally a big fan of putting information that relates to the code in a commit message. In theory you could always git blame to find out why something was written, but in theory code can exist outside of its repository, so using a comment puts the explanation closest to where it's needed.
A commit message should generally focus on the what was changed more than the how does the code work, so there I'd care more about seeing 1. that it's a hotfix 2. what it does to fix it and 3. some context as to why this was necessary.
Coming up with a good concise example of a comment for a dev post while still showing off the subtleties of writing good comments is definitely hard, and my 3 lines are already about the shortest example I could come up with (out in the real world, these things often get a lot longer).
But I think your examples did just get a little too simple here and there and you lost some of the points you were making in the text (which I mostly agree with, by the way).
Thanks for the feedback.
Totally agree with this.
Agreed. I also like the
Hotifx
in the 3rd code blockAnother valid use case: TODO-comments
definately- check out my blog on how to ensure these actually get done dev.to/grantdotdev/tech-debt-code-...
I agree with you that comments are stigmatized and we should be using more of this tool.
But where I disagree:
Complex code can use comments, but you should revaluate if this complexity is worth it, and if you could externalize its maintainancy. It may be better to add a sort package into your dependencies than create your own sort.
Overly verbose comments can be crucial. It should be used sparingly, but don't detract from your comment when trying to be succinct. It's better to have a paragraph than to not understand it.
A major argument I see against comments of that they are unmaintained by default. Comments should be updated, but nothing is making you update it. Just saying "don't let your comments be unmaintained" kinda skips this point placing the burden into the dev, and I miss this being addressed in your text.
I agree with most of your arguments, but had to say this
I agree with you. However I think the way we want to convince ourselves, what is right and what is wrong, is somehow corrupted. Since you're trying to make sense out of it, and give this some reasonable explanation you've just entered battle you can't really win.
For example somebody pointed out this code:
is just simply redundant information... and it IS!
But I clearly see your point, and would advocate for this case.
We're trying to use some sense and logic in this debate... but comments are essentially way of communicating from one developer to another. And there are no mathematical rules in the way we speak. We usually rely on our guts.
And that's my point, we should just follow our intuition, still debate, but approach it more like a telling a story.
For me we should loosely follow those rules:
- use comments, but don't overuse them
- avoid stating obvious
- keep in mind comments need to be up to date
- comments are great for pointing out important context of code
- don't trust comments, there are only hints
And lastly it's not really that important. If you can give someone 15min advice how to improve commenting skills, go ahead. But lately I've met people who spend absurdly large amount of time correcting others how they should comment... and that's just counter-productive.
Comments are great when you're working in legacy code. In situations where you have full control of the code from top to bottom, write code that is clear, not clever.
I like this take, and your piece is very well written!
This is a great article, I love it! Thanks for writing.
Code comments may not be evil but the developer writing the comments may be! 😶🌫️
One case you don't mention is type hints. If you can document the type of something with native type hints, that's always a better option, and documenting it with comments as well is redundant unless there's additional information that can be expressed that way that can't be expressed with native type hints.
As type hints are tightly coupled with Python language, and this article is predominantly aimed at a more generic level, I feel it would be misplaced and irrelevant.
Actually I wasn't referring to Python in this context. I work mostly in PHP and that's what I had in mind in my response.
Tools like Psalm let me express a lot more information about types than the native types can. For instance, I can specify that a string is the name of a class or interface, which can't be done with native type hints in PHP.
🤡