It is essential for any programmer to know when it is necessary to write a comment in the code and when it is better to avoid it, as well as in case one decides to write it, do it with the minimum possible words, straight to the point, and that helps other colleagues to know what is happening in very few seconds.
In my opinion, there are two big mistakes in this field, the first is to believe that commenting on everything or almost everything is good, when in reality we are creating an excess of redundant information. The second is to think that the context is always enough to understand the code and that comments should never be written, giving rise to parts of the code whose cognitive difficulty is high enough to justify a comment that reduces said complexity. For this reason, I believe that, as Aristotle said, virtue lies in the middle ground.
But before delving into this topic, we are going to review the basic and syntactic fundamentals of how to write comments in Dart, based on the Effective Dart guide.
Write good documentation
In the documentation section we can see a detailed explanation of how to write comments.
This section explains that to write simple comments that we do not want to include in the generated documentation, for example some explanations about what a line of code does within a method, we would use the double slash //
, while those comments that explain what a method does, or what a property is for or what a class does (these three groups should be in a generated documentation) we would use the triple slash ///
.
/// Saves the given [user] if all its mandatory parameters are set.
Future<void> saveUser(User user) async {
final messenger = ScaffoldMessenger.of(context);
late String message;
// Due to a db restriction, check if the name is set before saving.
if (user.name != null) {
await database.persistUser(user: user);
message = 'User successfully saved';
} else {
message = 'You must set a name for the user before saving it.';
}
messenger.showSnackBar(
SnackBar(
content: Text(message),
),
);
}
It is important to note that, unlike other languages, the use of block comments /* ... */
is restricted only to blocks of code that we want to temporarily disable, the rest should always use line comments either with //
or ///
.
In order to be able to generate high-quality documentation, the guide explains how the general structure of a comment block should be. This should contain a single line as a summary of what the code does, followed by a block of text where we can explain in a little more detail what we think is appropriate to explain. We are also recommended to provide a code example in case we see it necessary, as well as links to other components in the project or external resources that can help to better understand what we are analyzing.
/// An alternative semantics label for this text.
///
/// If present, the semantics of this widget will contain this
/// value instead of the actual text. This will overwrite any
/// of the semantics labels applied directly to the [TextSpan]s.
///
/// This is useful for replacing abbreviations or shorthands
/// with the full text value:
///
/// `\``dart
/// Text(r'$$', semanticsLabel: 'Double dollars')
/// `\``
final String? semanticsLabel;
Another big difference between Dart and other languages is that while in other languages we would write a comment like this:
/**
* Parses the provided parameters and constructs a model accordingly.
*
* @param id: Id of the user
* @param name: Name of the user
* @param surname: Surname of the user
* @return A UserModel created from the provided params.
* @throws Exception An error if any parameter is empty.
*/
fun createModel(id: String, name: String, surname: String): UserModel {
// [...]
}
In Dart it would be written like this:
/// Parses the given [id], [name] and [surname] to construct
/// and return a [UserModel] object.
///
/// Can throw an [Exception] if any parameter is empty.
UserModel createModel(String id, String name, String surname) {
// [...]
}
As you can see, it is favored to include the parameters and other relevant data within the narrative of the comment itself, which helps the comment to be shorter and probably more didactic.
In general and from my point of view, I think that Dart's vision of how to write comments is quite elegant and efficient, and I invite you to take a look at the official guide to understand all its peculiarities.
However, one thing is to know how to apply a guide to be able to write a comment with the format it should have, and another is to understand when a comment is necessary and when it's not, so now that we have seen the basic aspects of the syntax, let's see how can we determine which code should be commented and which not:
When to comment
The general rule to know if it's necessary to add a comment would be the following:
In the event that a colleague saw this code, would they be able to understand in a general way what it's for just by seeing its description, parameters, or other nearby information; in less than 5 seconds?
If the answer is yes, adding a comment is probably unnecessary. Otherwise, I would advise you to either add a comment or refactor that code to make it easier to understand. Actually, I think that this last option is preferable to writing a comment, in my opinion the code should be as short and concise as possible.
Let's put this exhortation into practice. See if you can understand the following code block in less than 5 seconds:
int sum(int first, int second) {
return first + second;
}
I think that quite likely you will not have needed even 2 seconds to understand it. This is a very clear example of when a comment like this is redundant and not necessary at all:
/// Returns the sum of [first] and [second].
int sum(int first, int second) {
return first + second;
}
Now look at the following block and try to understand it from the code:
Future<void> initialize({bool addAdditionalDelay = false}) async {
if (addAdditionalDelay) {
state = LauncherState.loading;
await Future.delayed(const Duration(milliseconds: _additionalDelay));
}
// Check legal acceptance.
final isLegalAccepted = await _preferencesRepo.isAcceptLegal();
if (!isLegalAccepted) {
state = LauncherState.acceptLegal;
return;
}
// Check internet access.
final connResult = await Connectivity().checkConnectivity();
if (connResult == ConnectivityResult.none) {
state = LauncherState.noConnection;
return;
}
// Get the API token.
String? token;
try {
token = await _sessionRepo.getToken();
_log.d('Obtained token: $token');
state = LauncherState.goToMain;
} catch (e) {
_log.e('Error while login: $e');
state = LauncherState.error;
}
}
This function does a few things, and while I think you will have been able to understand each piece, I also think that one will probably need more than 5 seconds to understand it. For this reason, and since everything is an initialization operation, adding a brief explanatory comment at the beginning can be very useful:
/// Perform the initializations operations.
///
/// Optionally set [addAdditionalDelay] to true in order to simulate
/// a delay.
///
/// This can be useful if the user is clicking on the try again button,
/// so they know that something is happening.
Future<void> initialize({bool addAdditionalDelay = false}) async {
// [...]
}
Conclusion
From here everything is experience and common sense. If you have recently started programming, don't worry too much about this either, and if in doubt I recommend adding a comment. I think this approach is better than doing nothing and driving the next guy crazy to see what's going on in that code.
With a bit of good practice, trying to dismember the code as much as possible, it is quite achievable to be able to write self-explanatory code in most cases. However, for all those minor cases or complex algorithms where there is no way to simplify further, it is better to write a comment.
Top comments (0)