As software projects grow, being able to demonstrate your intention to other developers through clear and reasonable code is paramount. In this series I cover short tips to do just that. The first article in the series can be found on my blog: Ruby's Tap.
Today we cover the bang(!) symbol. In my opinion is one of the most common, yet misunderstood tactics for communicating intent to other developers. While subtle, it can actually be pretty useful when used consistently.
The first thing to remember is that the
! in ruby, at the end of a method name, such as
save! does nothing special on it's own. It forms part of the method name and it's only purpose is to communicate something else about what that method might do.
These all tell you that the bang symbol simply modifies the object in place. It certainly does do this in some cases, but it is definitely not what bang means. An example of this is
downcase! will modify the existing string, while
downcase will return a new string.
This is useful, however, the logic falls down very quickly. For example, the
a = [1,2,3] a.pop a #=> [1,2]
pop modified the array, but has no
Another example is the
save/save! methods in Rails.
nil on a validation error, while
save! throws an error.
In the Workflow gem, you use
state! to move to a particular state.
None of these methods have anything to do with modifying the object, so why are they using bang?
Luckily Matz - the creator of Ruby - has something to say on the issue:
The bang (!) does not mean "destructive" nor lack of it mean non destructive either. The bang sign means "the bang version is more dangerous than its non bang counterpart; handle with care". Since Ruby has a lot of "destructive" methods, if bang signs follow your opinion, every Ruby program would be full of bangs, thus ugly.
~ Yukihiro Matsumoto
So there we go. Use bang when you have a method that has the potential to be more dangerous. I find it nice to leave a comment on any bang method, explaining the danger.
Some good use cases for the bang:
- Throwing an error on failure instead of returning nil.
- Forcing a fetch to bypass cache and recalculate.
- A method that hides a lot of complexity, or has performance implications.
- Any other method that has may cause side effects that may not be expected.
Bang can be a fantastic tool for letting other developers know to proceed with caution, so use it when it makes sense. When exploring code, take special care to look out for these methods and ensure they are being used correctly. They can often reveal bugs or performance issues if the danger has not been respected.