---
title: Your Program is a Special and Unique Snowflake
teaser: Simplify and clarify your code by carefully choosing abstractions.
tags: good code,ruby
author: Joe Ferris
published_on: 2016-05-26
---

> Everyone's special...which is another way of saying no one is.

&mdash; The Incredibles

Developers live in a world of abstraction. Rails abstracts away the details of
web requests, providing you with a world of routes and controllers in place of
requests and responses. Ruby hides the fact that you're constantly allocating
and deallocating memory. Ruby doesn't have to tell the computer how to allocate
memory, though, because it's written in C.

We can't live without abstractions, because there's too much detail in our world
to hold in all at once. We have to build bigger concepts out of smaller concepts
until we can fit them all in our human brains.

One of the great challenges of writing usable code is deciding what and when to
abstract. We want just enough abstraction that we can hold the problem in our
heads, but not so much that we can't tell what's actually going on.

One technique for keeping that balance is to use fewer abstractions by avoiding
special cases. Specializations can be easy to learn because they're more
concrete, but we need more special cases to solve most problems because each
special case only applies to a specific situation.

## Custom Syntax

Let's look at a special case in Ruby:

```ruby
for user in users
  puts user.email
end
```

The `for` loop is rarely used in Ruby. Most authors prefer to write:

```ruby
users.each do |user|
  puts user.email
end
```

Why? One is a special case, and one is an abstraction.

The `for` loop introduces new syntax and keywords for a single concept. The
contents will be performed for each item in a list. Once you've learned how
`for` loops work, you know how to do one thing. You've gained one ability for
one keyword. That's not a great return on your investment. If you're going to
fit a lot of programming concepts in your head, you'll need to do better!

The `each` method builds on widely-used concepts from Ruby: methods and blocks.
To truly understand `each`, you need a solid understanding of some more abstract
concepts. This takes longer, but you'll be well-rewarded for your efforts:
almost every problem in Ruby is phrased in terms of objects, methods, and
blocks.

Another nice aspect of the `each` abstraction is that we can build it ourselves
in Ruby:

```ruby
class Array
  def each(&block)
    unless empty?
      first, *rest = self
      block.call(first)
      rest.each(&block)
    end
  end
end
```

Our implementation introduces some new concepts we have to understand:
conditionals, the `unless` keyword, the `empty?` and `call` methods, array
globbing, recursion, and block arguments. Veteran Rubyists may not realize how
many concepts are involved in what we consider a basic building block of
application logic, but you had to learn every one of these concepts at some
point to use them effectively.

On the other hand, we can't implement a `for` loop ourselves. We can't break it
down any further, because it's a special syntax and doesn't build on other
concepts from Ruby.

If you can implement a new idea in terms of other ideas that are already
defined, you don't need as many abstractions in total. How many ideas can you
eliminate from your program by building and reusing abstractions?

## Empty Cases

Many abstractions are just a more specific version of another existing
abstraction. Let's look at a popular example: `nil`.

We use `nil` to represent something that could be there, but isn't. Some users
may have emails, while others don't. Those users will return `nil` when asked for
their email.

```ruby
User.new(email: "user@example.com").email
=> "user@example.com"

User.new(email: nil).email
=> nil
```

This seems simple and we need to represent the case of a missing email address.
However, this greatly complicates our code. We can't just ask for an email and
use it; we have to verify that it exists:

```ruby
users.each do |user|
  unless user.email.nil?
    UserMailer.update(user.email, update).deliver_now!
  end
end
```

This concept comes with a slew of other concepts and techniques for working
around it: `nil?`, `present?`, `try`, and now even a special syntax (`&.`).

This concept may be worth its cost in some situations, but can we sometimes do
without it?

In our email case, how different is a nullable email from an array of emails?

```ruby
User.new(emails: ["user@example.com"]).email
=> ["user@example.com"]

User.new(email: []).email
=> []

users.each do |user|
  user.emails.each do |email|
    UserMailer.update(user.email, update).deliver_now!
  end
end
```

Our usage patterns are very similar, but the second example removes an
abstraction entirely. Now we don't worry about arrays and nils; we only worry
about arrays.

This works because `nil` is a specialization of `Array`. An `Array` represents a
situation where there is an unknown number of something. A `nil` represents a
situation where there could either be one or zero of something.

Sometimes `nil` may be just the right abstraction, but there are many ways to
represent an empty case. A signed out user can be `nil` or an instance of a
`Guest` class or an unsaved instance of your normal `User` class. Which
introduces the best balance of low abstraction and low confusion for your
application?

## Conditionals

Every conditional is a special case the users of a codebase will need to parse.

We can use more common abstractions to avoid them:

```ruby
# Conditional
users.each do |user|
  unless user.email.nil?
    UserMailer.update(user.email, update).deliver_now!
  end
end

# Abstraction
users.map(&:email).compact.each do |email|
  UserMailer.update(email, update).deliver_now!
end
```

Using a Null Object, we can introduce another abstraction to eliminate many
special cases:

```ruby
class Guest
  def admin?
    false
  end

  def can_edit?(post)
    false
  end

  def name
    "Guest"
  end
end
```

With this class in place, views will no longer need to check whether a user is
signed in. We can decide how guests behave in one location and reuse that
abstraction.

## Folds

Many computations can be performed using an abstraction called "folding,"
represented by the `reduce` (aka `inject`) method in Ruby:

```ruby
# Using special cases
class Game
  def score
    result = 0
    rounds.each { |round| result += round.score }
    result
  end

  def competitors
    result = []
    rounds.each do |round|
      round.users.each do |user|
        result << user.email
      end
    end
    result
  end
end

# Using abstraction
class Game
  def score
    rounds.reduce(0) { |result, round| result + round.score }
  end

  def competitors
    rounds.reduce([]) { |result, round| result + round.users.map(&:email) }
  end
end
```

In this case, there is another level of abstraction built on top of a fold:

```ruby
class Game
  def score
    rounds.sum(&:score)
  end

  def competitors
    rounds.map(&:users).flat_map(&:email)
  end
end
```

Using `sum` and `flat_map` introduce a specialization of a fold. In this case,
which do you think is easier to understand?

## Wrapping Up

> Special cases aren't special enough to break the rules.

&mdash; The Zen of Python

Almost everything you use in programming is an abstraction, but the trick is to
decide how abstract you want to be. When coding try making your programs more
abstract to see if you can eliminate special cases. Is it harder to understand
without the clarity of specialization, or is it easier to follow because it uses
a single level of abstraction?
