The first time I heard about comments, I was told it is meant to document why a code is what/how it is. As nice as it sounded to me, it didn't make so much sense, probably because I was just starting my programming journey.
But it is in fact the reason for comments, a comment that explains what a code is or how a code works simply indicates a bad code.
You wouldn't have to explain what a code does if the code already explains what it does, and how it does it. Here is an example
// get signed in user
const getUser = () => {
return { name: 'John', age: 22 };
};
What do you think of the comment in the example above? Do you think is necessary? In my opinion, this comment is so unnecessary.
But here is the thing, the developer probably felt he/she should add the comment there because if it should be omitted, then a reader (maintainer/developer) would find it hard to understand which user the function - getUser
is getting.
So because the author (developer) is trying to be good, he/she would leave a comment there to make sure whoever maintains it in the nearest future (author included) would not find it hard to know "what" the getUser
function does.
So Elijah, how is the comment unnecessary?
Having comments like this all around your project is blatantly bad for maintenance. Because here is the thing, developers hardly maintain comments. Most of us forget it's irrelevant to update a comment after updating its code or we simply forget most times. But that's what leads to bad codes.
Here is an example, if the purpose of the getUser
function is to change to getting any random user, let's say by id
, then we'd most likely now have
// get signed in user
const getUser = (id) => {
return users.find((user) => user.id === id);
};
Here is the scenario:
When a reader sees this function being called elsewhere, he/she would clearly have to come to where the function is declared (which could be in a different file) just to know exactly what the code does.
The reader looks for where the getUser
function is declared because the function name getUser
doesn't really give the full picture of what the function does.
So he/she sees the function and the comment get signed in user
and probably goes back with a conclusion of what the code does (which is a wrong conclusion) or gets confused because there is probably another function that gets the signed-in user.
So the thought of "is the code redundant?" comes to the reader's mind, the reader would now have to read the two functions explicitly to know which is which.
This is bad code, especially when they are tons of developers maintaining the project. Because if the first reader finally understands what the getUser
function does and doesn't change the code to make it clean, any other reader would go through the same thing the first reader went through.
So what should we do with situations like this?
Get rid of the comment and write a clean code, the first example could be
const getSignedInUser = () => {
return { name: 'John', age: 22 };
};
and refactoring into the second example could be
const getUserById = (id) => {
return users.find((user) => user.id === id);
};
When a reader sees getSignedInUser
elsewhere, he/she already knows what the function does.
What comments should be for
When your comments are to only explain why a code works the way it does, you'd have little or no use for comments at all.
Here is a simple example from my library (native-json-db) that I refactored. Because I had to refactor most of my projects (that are still in maintenance) after I got a grasp of what my comments should be for and how to make them right:
// verify the newData is an object, useful for javascript users
if (typeof newData !== 'object' || Array.isArray(newData)) {
return {
message: 'New data must be an object',
error: 'INVALID_DATA_TYPE',
errorCode: 611,
};
}
In the example above, there is a "what" and a "why" in the comment. The "what" being verify the newData is an object
, and the "why" being for javascript users
. A reader who sees the condition should already know the code verifies the newData
is an object, but the question of why comes to mind. Because this is a typescript project, so why did the author have to validate the newData
this way.
If there isn't a comment that explicitly states a reason, the reader may just end up removing the whole condition and then we'd have a bug. So refactoring the comment should only leave the for javascript users
there.
While refactoring the code in this library, I found where I resolved a promise when an error occurred rather than the usual reject that I've been using throughout the code. But then I didn't write why I decided to resolve rather than reject.
And now, I don't remember what made me resolve rather than reject. I know it was intentional. But had it been I commented the reason, I wouldn't have had to struggle with my decision there for a minute.
So for decisions you make that you know would lead to a question of "why did I do this like this", or "why did he/she do this instead of this", you should do well to state your reason precisely, to save everyone's time
Necessary comments
There are some cases where comments other than "whys" comments can prove useful, like in tutorials. i.e places that require further explanation and wouldn't require maintenance per se.
Nonetheless, when you create a project that you're sure nobody is coming back to, it is also a good practice to write clean code as much as possible.
Personally, when I work on such projects for learning purposes, I still wanna make sure it is clean and I don't have unnecessary comments. Because I don't want to get used to writing bad code with unnecessary comments.
So I'd recommend using every other type of comment explicitly for teaching purposes for beginners.
Other common necessary usages of comments are in license, and todos (which you shouldn't forget to get rid of when done).
Some common bad comments
Apart from it being generally frustrating, some comments have specific frustrating features. There are several of them, but the common ones are misleading comments, redundant comments, and commented codes.
After refactoring a ton of my projects, these are some of my errors and observations.
Misleading comments
All bad comments can be misleading, but some are definitely misleading. for example;
// updateMany options
interface UpdateManyOptions {
updateAll: boolean;
}
// updateMany options
interface DeleteManyOptions {
deleteAll: boolean;
}
In this example, I (the developer) simply copied the UpdateManyOptions
block of code (including the comment) for the DeleteManyOptions
just for me to forget to update the comment. And as such the comment is quite misleading. Besides both comments are redundant.
Another misleading comment I had was this
// find object with the key value
findOne(filter: Object): Promise<Object | null> {}
In the findOne
function, there is no key
value being passed as a parameter nor is there a variable called key
. So that can get confusing for some readers. You could say alright, to not make it misleading let's refactor the comment to find object with filter provided
, but then would the comment really be necessary? No, it wouldn't. So Elijah, please get rid of that.
Redundant comments
Like in the example in the previous section, the name of the interface UpdateManyOptions
already explains what the interface is for. So what's the essence of the comment? I have a couple of redundant comments like this
// get the whole data in d document
get allData(): Promise<Object[]> {
return new Promise(async (resolve) => {
resolve(this.dataArr);
});
}
The thing about redundant comments is that you'd have to learn about what the code does twice. Because you'd see the comment first and be like get the whole data in d document
and end up seeing the function saying the same thing. Why the stress? Just get rid of the comment if the code already explains itself.
Commented codes
This is quite common. But gone are the days where you'd need a code you didn't need before and start crying about it because you deleted it rather than simply commenting it out.
With tools like git, you can keep track of every code change you made in your project. So if you don't need a code now, delete it. When you need it, go to the git history of the project and get it.
But the problem with this is when you don't commit every change you make to your code. So you should endeavor to commit every change made to your code, I'm starting to do this too. It's been really helpful for me.
Another common bad comment
How to get rid of bad comments
Try not to get me wrong here, the only reason you should get rid of the "whats" or "hows" comment in your code is that your code should already explain what it does and how it does it.
For example;
// expires in 10 days
const e = 60 * 60 * 24 * 10;
The comment there shouldn't be there. But believe it or not, it's quite helpful there. Because if you remove it, a reader would have no idea what the variable e
is for.
So it's one thing to get rid of all the unnecessary comments in your code, it's another thing to refactor your code to not need those comments in the first place:
const expiresIn = 60 * 60 * 24 * 10;
So as a good practice, when you're refactoring; first refactor your code to not need the comment before getting rid of the comment.
Now refactoring your code to not need a comment can be challenging at first, but with time you'd get used to having tons of ideas for refactoring. But there are some good practices you can use to start out;
for example;
// get all users and sort them by their names alphabetically
const getUsers = async (username, password) => {
const res = await fetch('http://localhost:5000/users');
const users = await res.json();
users.sort((a, b) => {
if (a.name > b.name) {
return 1;
} else if (a.name < b.name) {
return -1;
}
return 0;
});
};
So if we want to refactor this to not use the comment, we may end up with a function name as getUsersAndSortThem
or sth similar. This is your function doing two things, which isn't the best thing for a function. Because when it comes to writing clean codes, as much as possible your function should only perform one task.
So you should break the function into two, where you'd have a getUsers
and sortUsers
function separately.
Here is another example of breaking things up to be more readable
const sortUsers = () => {
// in comparison, if name of first param is greater than name of second param return 1, if opposite return -1, else return 0
users.sort((a, b) => (a.name > b.name ? 1 : b.name > a.name ? -1 : 0));
};
This developer knows that reading the users.sort
line would take time to fully understand what's going on there (because it's not really readable), so he/she decided to help with an overwhelming comment. A simpler fix would've been to break the ternary operators to if statements to become more readable.
In most cases, it is often best to not use more than one ternary operator, because it'd be hardly readable.
All these are what make refactoring codes interesting. But if you don't know how to write clean readable code, removing comments (that usually solve your problem of writing bad code by explaining what's going on) becomes an even bigger problem.
So read books, or articles on how to write clean codes, and learn from superiors. I recommend reading the book - Clean code by Robert Ceil Martin.
Conclusion
Comments are an important part of your code, they indicate if you've not done so well in your code as well as indicate if you've done well. If you've done well, trust me you'd hardly have use for comments, and if you haven't you would have comments on almost every part of your code, trying to make up for the bad code you've written.
But picking up this new habit of writing clean code doesn't happen overnight. Get into your past projects, and refactor as many as possible. Check out some open-source projects and do what you can there. And soon enough you'd get the hang of it. I'm still getting the hang of it myself.
Alright, that's it for this. Thanks for reading. Please if you enjoyed this article hit the like button and leave a comment for me. You can also follow up with more articles like this by following me here and on Twitter @elijahtrillionz.
And yeah, you can buy me a coffee to support my writing as well. A dollar can go a long way in cheering me up. I appreciate.
Top comments (6)
Really interesting post!
Thanks, happy it interests you
Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍
Happy new year everyone
It annoys me how frequently you see stuff like
getProjectFromDatabase(projectId) /* gets project from the database by its ID */
in beginner tutorials. Like sure, commenting more heavily than normal is appropriate for a beginner audience, but that doesn't mean you have to treat them like idiots.Some good points here.
As you've mentioned, there are some comments that really are necessary. I remember I worked alongside a team once. They were scratching their heads over why something worked on their machines but didn't after deployment.
It turns out there was a bug in the compiler for Release configurations only, and they had to do a workaround.
stackoverflow.com/questions/378303...
In addition to 'why', I think comments that document unexpected outcomes deserve a place to stay too.