Let’s say you have some code whose intent is clear to you but you can see a world where someone else may be confused. Often after writing such code, you realize this and add some comments to clarify the intent.
We add code comments for other developers who will interact with whatever we wrote, because we are courteous and thoughtful teammates:
class HostsController < ApplicationController def show # make sure user belongs to host if name is internal # set current host in session if current_user.hosts.include?(host) && host.match(/intranet.example.com/) session[:current_host_id] = host.id redirect_to(projects_path) else raise 'something went horribly wrong oh nooooo' end end end
The Telephone Game
Remember the telephone game? Messages passed through intermediaries can get garbled in transmission. Particularly unreliable, logic-free intermediaries like code comments.
On their face, comments should be super helpful - I mean, you’ve left helpful notes for the next person! Isn’t that a good thing?
Yes, although now we have duplication - the comment and the code itself both speak to what this bit of logic should do.
Comments are a code smell which means “something may be improved here and we should dig deeper to see if that’s true.” In this case, the smell is “hey something is probably more complicated than it needs to be.”
Later on, when someone moves the session-setting behavior somewhere else, they have to remember to move this comment or update it. As humans, this is easy to forget.
Instead, let’s use intention-revealing method names to encapsulate the behavior. We’ll also move this logic into private methods since we don’t want other classes calling these methods:
class HostsController < ApplicationController def show if user_belongs_to_host? && host_name_is_internal? set_current_host_in_session redirect_to(projects_path) else raise 'something went horribly wrong oh nooooo' end end private def user_belongs_to_host? current_user.hosts.include?(host) end def host_name_is_internal? host.match(/intranet.example.com/) end def set_current_host_in_session session[:current_host_id] = host.id end end
Other Smelly Comments
- TODOs, like
# Remember to fix this terrible method
- Commented out dead code. Just delete it - that’s what Git is for.
- Comments which restate the method name in English.
Comments are one of the code smells we address in our Ruby Science ebook.
When Are Comments Useful
This isn’t a hard and fast rule. Some comments are useful:
- Class-level comments: Adding a comment at the top of a class to describe its responsibilities can be helpful.
- Open-Source: Ruby Gems and other open-source libraries are good places to add more detail, because we can use tools such Yard to automatically generate documentation from the comments. Here’s an example in Paperclip. If you’re providing a library for others to use, lightly commenting the public interface is typically encouraged so that documentation for the library can be auto-generated. Here’s an example in Golang.