Best Use Cases for Code Comments

Learn the right way to use comments…

ilyas ipek
Dev Genius

--

In my +5 years of experience, I have noticed that developers -especially the experienced ones- believe that the perfect code requires no comments at all, but they also almost believe that no comments in your code mean it’s perfect :)

I agree that the code should be self-explanatory without comments, yet I also believe that well-utilized comments make the code more understandable.

In this blog, we will explore the valid use cases for comments that will improve the readability of your code.

1- Fixing weird bugs or edge cases

The general rule is to add comments when the “why” of the code existence is not understandable. So when you think the next developer -or you- will go “Why on earth was this piece of code added” then you should add a comment for it or even better the link to the task. For example…

fun processPayment(amount: Double) {
// Workaround for a known issue in the PaymentLibrary v2.3.
// The library fails to process amounts exactly equal to $1000 due to an internal bug.
// See Ticket #123 (or add a jira link) for more information.
if (amount == 1000.0) {
// Temporarily adjust the amount to bypass the bug
processPayment(999.99)
processPayment(0.01)
} else {
// Normal processing
PaymentLibrary.process(amount)
}
}

// or
fun doSomething() {
// Workaround for a X issue in Android SDK version 29 where XYZ feature malfunctions.
if (androidSdkVersion == 29) {
applyWorkaroundForSDK29()
}
}

2- Explain a business logic

Sometimes the business logic is not understandable, especially in a complex one like payments or editors, etc. So it’s a good idea to add some comments to explain why the code is doing what it’s doing.

fun handleUserLeavingChannel(userId: String, channelId: String) {
if (isLastAdmin(userId, channel)) {
// If the user is the last admin, they must assign another member as admin before leaving.
// This is crucial to ensure that the channel remains properly moderated and managed.
throw AssignAdminRequiredException("You must assign a new admin before leaving as you are the last admin.")
}

removeUserFromChannel(userId, channel)
}

3- Provide code overview & examples

Complex algorithms such as calculations or animations can be hard to understand by looking at the code without grabbing a pen, it’s pretty useful to add an explanation or even examples to these codes.

// Length of arc is angle * radius
// Angle (radians) is length / radius
// The length should be the same as the stroke width for calculating the min angle
val strokeCapOffset = (180.0 / PI).toFloat() * (strokeWidth / (CircularIndicatorDiameter / 2)) / 2f

Warning: This kind of commments might go out of sync and can be misleading instead of being helpful, so make sure to update them when you change the code.

4- Explain WHAT code does instead of using sub-functions

I know this might sound against the whole “clean code” philosophy which recommends splitting your code into sub-functions that have a single responsibility bla bla bla…

But sometimes it’s better to read/debug most of the code like an article rather than going through 200 functions to find where you need to make changes.

In this case, you can add a comment to tell what a chunk of code is doing so the reader can skip that piece if they aren’t interested in it.

Here is an example from a Google libandroidx.lifecycle.SavedStateHandle

private val savedStateProvider =
SavedStateRegistry.SavedStateProvider {
// Get the saved state from each SavedStateProvider registered with this
// SavedStateHandle, iterating through a copy to avoid re-entrance
val map = savedStateProviders.toMap()
for ((key, value) in map) {
val savedState = value.saveState()
set(key, savedState)
}
// Convert the Map of current values into a Bundle
val keySet: Set<String> = regular.keys
val keys: ArrayList<String> = ArrayList(keySet.size)
val value: ArrayList<Any?> = ArrayList(keys.size)
for (key in keySet) {
keys.add(key)
value.add(regular[key])
}
bundleOf(KEYS to keys, VALUES to value)
}

Notice that you could have split this into subfunctions like…

SavedStateRegistry.SavedStateProvider {
saveStateFromProviders()
convertToBundle()
}

private fun saveStateFromProviders() {
val map = savedStateProviders.toMap()
for ((key, value) in map) {
val savedState = value.saveState()
set(key, savedState)
}
}

private fun convertToBundle(): Bundle {
val keySet: Set<String> = regular.keys
val keys: ArrayList<String> = ArrayList(keySet.size)
val values: ArrayList<Any?> = ArrayList(keys.size)
for (key in keySet) {
keys.add(key)
values.add(regular[key])
}
return bundleOf(KEYS to keys, VALUES to values)
}

Subfunctions might look great but it creates more problems than it solves.

1. Now there are so many functions to navigate into to understand the code so in my opinion now it’s less readable.
2. You need additional work to pass any parameters between these functions in the feature.
3- About half of your unit tests are meaningless, as they examine functions that aren’t useful in isolation.

The decision between splitting a big function into subfunctions, incorporating comments, or well-named variables should depend on the use-case and the art is to write a readable code by using these tools.

5- TODO comments

TODO comments help mark work that still needs to be done, like when you’re coding with someone else and need to hand off the task to them.

fun fetchUsers() {
val users = fetchUsersUseCase()
...

// TODO: show users in the ui
}

However, it’s usually better to finish all tasks before merging your code. If a big feature is missing, create a ticket or Jira task instead.

// TODO: Implement caching mechanism to improve performance
fun fetchData() {
// ...
}

As a rule of thumb, use TODO comments for quick reminders, but for anything bigger, make a ticket and let the Product team know about it.

Important Notes

1- Good comments do not excuse unclear code.
2- Don’t write comments for obvious code for the love of god.
3- Keep comments short and descriptive.
4- Keep comments up to date or delete them

Conclusion

Use comments effectively to make your code more readable and minimize WTF moments for others.

--

--

Android developer @teknasyon, writes about Android development and productivity.