DEV Community

Cover image for Stop Being So Negative
Jesse Spevack
Jesse Spevack

Posted on

Stop Being So Negative

Stop Being So Negative

My brain hurts and I cringe a bit whenever I stumble across an unnecessarily complicated negative condition. Also, to be perfectly clear and honest, I never have written anything that remotely resembles any of these examples. Why would I say I've written terrible code when I've never written terrible code?

Let me show you what I mean:

# Is the collection NOT empty?
if !collection.empty?
Enter fullscreen mode Exit fullscreen mode

or

# Is the thing NOT nil?
if !thing.nil?
Enter fullscreen mode Exit fullscreen mode

Worse still is when a negative condition is paired with an unless.

# UNLESS the collection is NOT empty, do the next thing.
unless !collection.empty?
Enter fullscreen mode Exit fullscreen mode

Or the universally agreed upon most heinous double negative in Ruby:

# UNLESS the thing is NOT nil, do the next thing.
unless !thing.nil?
Enter fullscreen mode Exit fullscreen mode

The point here is that for some reason negative logic always seems tougher to understand than its positively phrased counterpart.


Ruby is too Expressive to Warrant the use of Negatives.

The human brain evolved hundreds of thousands of years ago to be good at a great many things, but tracking negative logic is not one of them. At least that's the case for my particular human brain.

Ruby is so expressive, we should never have to write tough to read nonsense code like if !collection.nil?.

Instead we should be writing our conditions with a positive voice.

A collection not being empty is the same as saying a collection with at least one element. A thing not being nothing is the same as saying a thing that exists.

Bad

if !collection.empty?
Enter fullscreen mode Exit fullscreen mode

Good

# present is a rails-ism
if collection.present?
Enter fullscreen mode Exit fullscreen mode

What if we are not using Rails? Well Ruby has some non-negative tools for us as well!

irb(main):001:1* collection = []
=> []
# Is there at least one element in our collection that is not 
# `nil` or `false`? In other, clearer, words are any of the
# elements in our collection truthy?
irb(main):002:0> collection.any?
=> false
Enter fullscreen mode Exit fullscreen mode

If our goal is to run some code when a collection is not empty, any? is a pretty good choice.

irb(main):001:0> collection = ['hello, world!']
=> ["hello, world!"]
irb(main):002:0> collection.any?
=> true
Enter fullscreen mode Exit fullscreen mode

There can be some weirdness, however.

irb(main):006:0> collection = [nil]
=> [nil]
irb(main):007:0> collection.any?
=> false
Enter fullscreen mode Exit fullscreen mode

I think present? in Rails behaves mostly how I expect it to, so that ends up being the tool I reach for most often especially because I am almost always writing Ruby in a Rails context.

[1] pry(main)> collection = []
=> []
[2] pry(main)> collection.present?
=> false
[3] pry(main)> collection = ['hello, world!']
=> ["hello, world!"]
[4] pry(main)> collection.present?
=> true

# Careful now. But, this kind of makes sense. There is something
# in the collection after all. That something just happens to be nothing.
[5] pry(main)> collection = [nil]
=> [nil]
[6] pry(main)> collection.present?
=> true
# :mindblown:
Enter fullscreen mode Exit fullscreen mode

Let's look at our nil? negative check. What are we really saying when we ask is the thing NOT nil?? We are really asking, does thing exist?

Bad

if !thing.nil?
Enter fullscreen mode Exit fullscreen mode

Good

if thing
Enter fullscreen mode Exit fullscreen mode

If thing can be nil, true, or false and we only want to proceed if thing is true or false, we should try:

# Again, stick to the positives.
if [true, false].include? thing
Enter fullscreen mode Exit fullscreen mode

In my experience, this could be filed in the edge case category as typically checking for falsey values is good enough.


Acceptable uses for Unless

There is only one acceptable use for unless and that is as a guard statement at the very start of a method. It may not use any compound, multi-clause logic. Nor may it be placed anywhere but the very first line of a method because we do not hate our colleagues.

Bad

unless condition
  ...
end
Enter fullscreen mode Exit fullscreen mode

Also Bad

def foo
  # some code
  ...

  unless something_happened_in_the_above_code 

  end
end
Enter fullscreen mode Exit fullscreen mode

The Worst

# This is an if
unless !condition
  ...
end
Enter fullscreen mode Exit fullscreen mode

Good

def foo
  return unless condition

  # mandatory empty line as a sign of humility.
  ...
  # remaining implementation that should only run if the condition is truthy.
end
Enter fullscreen mode Exit fullscreen mode

Let's make our code bases more positive places for all involved. Keep the negativity to a minimum.

Top comments (0)