Upgrading some Rails apps is as easy as bundle update rails.

But what if you have one of the other kind of apps? The ones that are still on Rails 4.0, or even 3.2, because you didn’t feel like dragging them kicking and screaming into the future?

Whether you’re facing a quick upgrade or a painful one, these steps will help you get your app to Rails 4.2 as smoothly as possible. And in the process, you’ll learn how to take full advantage of Rails 4.2’s new features.

Read the upgrade guide

You should always start your Rails upgrade by reading the Rails Upgrade Guide.

The upgrade guide will walk you through most of the major changes between Rails versions. It’ll tell you exactly how to change your app to fix the biggest problems you’ll run into. (For example, this change in Rails 4.1 would have caused us hours of pain if we hadn’t known to watch for it.)

But more than that, the upgrade guide will explain why you should make those changes. And that’s important, because it helps you understand which of its suggestions you should follow, and which you can ignore.

And if you want to understand the Rails 4.2 changes more deeply, read the release notes. They’ll introduce you to all the cool new Rails 4.2 features you’ll soon be able to use. If nothing else, it’ll motivate you to get that upgrade done.

Upgrade in steps

When you’re moving to a new Rails version, always upgrade to the newest point version first. And don’t skip minor versions.

So if you want to upgrade from 4.1.6 to 4.2, go 4.1.6 to 4.1.9, and 4.1.9 to 4.2. If you’re starting at 4.0.12, go from 4.0.12 to 4.0.13, then 4.0.13 to 4.1.9, and then from 4.1.9 to 4.2. It might seem like a lot more work, but it’ll save you time in the long run.

Why?

The newest point version of a Rails release has the best deprecation warnings.

Don’t underestimate how useful deprecation warnings can be. If you upgrade in steps, they’ll tell you exactly what will break and how to prevent it. If you jump too far, your app will just plain break, and you’ll have no idea why.

Finally, if a point version Rails upgrade (like from 4.0.9 to 4.0.13) breaks a test, it’ll usually also break during a major or minor upgrade. But those problems are much easier to debug and fix when less has changed.

So here’s what to do:

  1. Upgrade to the newest point release of whichever Rails version your app is on.
  2. Upgrade any other gems that keep you from bundle updateing.
  3. Fix the tests, committing as needed.
  4. Fix the deprecation warnings, committing as needed.
  5. Upgrade to the next minor or major version of Rails, and repeat.

Lean on your tests

When you upgrade Rails, your tests will be your first sign that something’s not right. They should be the first thing you run, and the first thing you fix. Don’t even bother trying to get your Rails app to boot before your tests mostly pass.

Upgrade your gems

When a test fails, and it doesn’t look like it’s your fault, it usually means one of your gems needs to be updated.

A Rails upgrade is just about the worst time to upgrade a gem. Since so much of your app is changing, it’s hard to tell if the Rails upgrade broke something, or a gem upgrade broke something, or if you broke something.

But because some gems rely so heavily on Rails internals, this is something you’ll just have to do. So if something inside sass fails, upgrade the sass-rails gem. And hope it doesn’t make things worse.

Take lots of notes

If you’re having a particularly rough Rails upgrade, you’ll have to experiment. You’ll probably do some debugging, tweak some code, or change some tests around.

As you do this, you will forget the changes you’ve made. Some of the things you tried that got you mostly, but not quite, there. Which tests you were going to look at next. Other places you need to change code. Tips to share with coworkers that are upgrading their own apps to Rails 4.2.

So, take lots of notes. Write everything down. Things you tried, and files you changed. Hypotheses you have about why things were failing, and whether they were correct. Which gems you had to update. Behavior that seems weird, and you’ll want to check out more closely later.

You don’t want to make a change, roll it back, realize it was actually the right thing to do, and not remember what you did. So write it down.

The best possible Rails upgrade

Some Rails upgrades are smooth. Things just work. Others are challenging, multi-day or multi-week projects. It depends on your app, your dependencies, and the specific things that Rails changed. But if you follow these steps, you’ll have the best possible upgrade experience.

Are you on Rails 4.2 yet? How did your upgrade go? Did you just have to update a bunch of gems, or are you still chasing down that one last failing test?

(A quick note: I’m going to release Practicing Rails on February 11th! So you only have a few weeks left to get the 25% early access discount.)

Monkey Patching. When you first try Ruby, it’s amazing. You can add methods right to core classes! You don’t have to call Time.now.advance(days: -1), you can write 1.day.ago! It makes Ruby a joy to read and write. Until…

You hit weird bugs because a patch changed Hash.

You get confused about which code actually ran, so you can’t debug it when it breaks.

And you finally figure out that all your problems were caused six months ago, when you monkey-patched Enumerable to make one line of code five characters shorter.

But what’s the alternative? No convenience at all? Code that looks like GroupableArray.new([1, 2, 3, 4]).in_groups_of(2)? Waiting for blank? to make it into core Ruby before you’re allowed to have nice user input handling?

You don’t want to give up on monkey patches entirely. But how can you write monkey patches that won’t make you want to fire yourself for incompetence the next time you see them?

Put them in a module

When you monkey patch a class, don’t just reopen the class and shove your patch into it:

1
2
3
4
5
class DateTime
  def weekday?
    !sunday? && !saturday?
  end
end

Why not?

  • If two libraries monkey-patch the same method, you won’t be able to tell.

    The first monkey-patch will get overwritten and disappear forever.

  • If there’s an error, it’ll look like the error happened inside DateTime.

    While technically true, it’s not that helpful.

  • It’s harder to turn off your monkey patches.

    You have to either comment out your entire patch, or skip requiring your monkey patch file if you want to run code without it.

  • If you, say, forgot to require 'date' before running this monkey patch, you’ll accidentally redefine DateTime instead of patching it.

Instead, put your monkey patches in a module:

1
2
3
4
5
6
7
8
9
module CoreExtensions
  module DateTime
    module BusinessDays
      def weekday?
        !sunday? && !saturday?
      end
    end
  end
end

This way, you can organize related monkey patches together. When there’s an error, it’s clear exactly where the problem code came from. And you can include them one group at a time:

1
2
# Actually monkey-patch DateTime
DateTime.include CoreExtensions::DateTime::BusinessDays

If you don’t want the patch anymore, just comment out that line.

Keep them together

When you monkey patch core classes, you add to the core Ruby APIs. Every app with core patches feels a little bit different. So you have to have a way to quickly learn those changes when you jump into a new codebase. You have to know where your monkey patches live.

I mostly follow Rails’ monkey-patching convention. Patches go into lib/core_extensions/class_name/group.rb. So this patch:

1
2
3
4
5
6
7
8
9
module CoreExtensions
  module DateTime
    module BusinessDays
      def weekday?
        !sunday? && !saturday?
      end
    end
  end
end

would go into lib/core_extensions/date_time/business_days.rb.

Any new developer could browse through the Ruby files in lib/core_extensions and learn what you added to Ruby. And they’ll actually use those convenient new methods you wrote, instead of those methods just getting in the way.

Think through the edge cases

I don’t know why Enumerable doesn’t have a sum method. So often, I wish I could write [1, 2, 3].sum, or ["a", "b", "c"].sum, or [Article.new, Article.new, Article.new].sum… Oh.

When you monkey patch a class, you’re usually thinking about one thing you want to make easier. You want to calculate a sum of numbers, but forget that Arrays can hold other things.

Right now, it makes sense. You’d never try to calculate the average of a bunch of hashes. But when you attach methods to an object that just plain fail when you call them sometimes, you’ll confuse yourself later on.

You can deal with this in a few ways. From best to worst:

  • Handle unexpected input reasonably.

    This works well if your patch works with strings. You can get something reasonable from almost anything if you call to_s on it first. And Confident Ruby will teach you a ton of ways to deal with different kinds of input.

  • Handle the error in a clearer way.

    This could be as easy as throwing an ArgumentError with a good message when you see input you’re not expecting. Don’t depend on someone else understanding random NoMethodErrors.

  • Document the kind of input you expect in a comment.

    The other two options are better, if you can use them. But if you can’t check for edge cases inside your patch, at least document them. That way, once your caller figures out it’s your patch that’s causing their problem, they’ll have an idea of what you were trying to do.

My all-time favorite monkey patch

Finally, I want to leave you with my all-time favorite monkey patch: Hash#string_merge:

lib/core_extensions/hash/merging.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
module CoreExtensions
  module Hash
    module Merging
      def string_merge(other_hash, separator = " ")
        merge(other_hash) {|key, old, new| old.to_s + separator + new.to_s}
      end
    end
  end
end

{}.string_merge({:class => "btn"}) # => {:class=>"btn"}

h = {:class => "btn"} # => {:class=>"btn"}
h.string_merge({:class => "btn-primary"}) # => {:class=>"btn btn-primary"}

It makes attaching CSS classes to HTML elements so much nicer.

Sensible monkey patching

Monkey-patching core classes isn’t all bad. When you do it well, it makes your code feel more like Ruby. But just like any of Ruby’s sharp edges, you have to take extra care when you use them.

If you keep your patches together, group them into modules, and handle the unexpected, your monkey patches will be as safe as they can be.

What’s the best monkey patch you’ve ever written (or seen)? Leave a comment and tell me about it!

You found the perfect solution to your crazy testing problem. All you have to do is override the DEFAULT_HOST constant, and you’ll be in business.

Except that you have to turn off warnings to get that ugly message to go away. But now all your tests pass, and you only had to change a few lines of code!

Except for that one test where you don’t want to override the host. But you could just re-override the constant, turn off the warnings again, and make sure it gets reset at the end of the test. You’re so close to being done, you can almost taste it!

Except… Except… Except…

And a few days later, when you get stuck for the twenty-seventh time and your app has become one giant ball of hacks, you’ll sit back and wonder: Why am I doing all this? Isn’t the solution worse than the problem?

How to solve the too-clever problem

It’s clear your original idea won’t solve your entire problem. So how do you think up a better idea, that solves all the edge cases your original idea couldn’t?

You can’t. You can’t fight too much cleverness with more cleverness. At least, not directly. Instead, go the other way. Go simple. Go straightforward.

What does that mean?

Inline the code you were trying to abstract away. Do repeat yourself. Keep your code explicit.

If you were trying to override a DEFAULT_HOST constant with a different default host, forget about the whole idea of a default. Just specify it every time.

So, instead of:

test/integration/welcome_test.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
require 'test_helper'

silence_warnings do
  Rack::Test::DEFAULT_HOST = "www.justinweiss.com"
end

class WelcomeTest < ActionDispatch::IntegrationTest
  include Rack::Test::Methods

  test "can visit the homepage" do
    get "/"
    # ...
  end

  # ...
end

Do something like:

test/integration/welcome_test.rb
1
2
3
4
5
6
7
8
9
require 'test_helper'

class WelcomeTest < ActionDispatch::IntegrationTest
  test "can visit the homepage" do
    get "http://www.justinweiss.com/"
    # ...
  end
  # ...
end

Whenever my seemingly perfect solutions break down, it’s because I didn’t imagine the edge cases I’d eventually have to handle.

It’s OK. We can’t predict the future. But when you notice it happening, stop digging. Don’t just apply patch after patch after patch. Instead, unwind your original solution and extract a better one.

How to extract a better solution

When you have all your code written out in an explicit, straightforward way, you’ll start to think of ways to reorganize it.

Usually, it’s enough to apply Extract Method or Extract Class in the right place. The trick is deciding what that right place is. But figuring that out is a lot easier when you see lots of repetition right in front of you.

And lean on inheritance and delegation. Those are the simple building blocks that will help you clean up your code without getting too clever.

One more thing

Don’t forget to read the documentation:

test/integration/welcome_test.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require 'test_helper'

class WelcomeTest < ActionDispatch::IntegrationTest

  setup do
    # This already exists:
    host! "www.justinweiss.com"
  end

  test "can visit the homepage" do
    get "/"
    # ...
  end
  # ...
end

The answer won’t always be that obvious. But there’s nothing more humbling than realizing there’s a one-method built-in solution to the exact problem you wrote three classes and a gem to solve.

A better solution, in the end

Your second solution will usually be better in every way than your original one.

Why is that?

  • You have more experience as a developer.

    So you’ll have a better idea of what makes good code.

  • You know more about the system you’ve built.

    So you can make better decisions about how the code you’re writing fits into it.

  • You know which of your assumptions were wrong

    So your solutions can better fit the actual problems that exist, not the ones you imagined might exist.

And in the end, there might be a place for the best of your cleverness to fit back in. This time, without the hacks.

You have to stop digging

Clever code is fun to write. In Ruby, it’s also easy to write. And it’s especially easy to keep going down a path, even when you know it’s taking you to the wrong place.

But once you get that nagging sense that something’s not quite right, stop for a minute. Un-factor your code. Read the documentation. Make your code straightforward and explicit. And find a better way.

Can you remember the last time you kept on digging yourself a hole that just wouldn’t end? How did you get yourself out of it? And what did the code you ended up with look like?

It’s so easy to get lost in the small bits of daily work you do. When you’re that focused, you can totally miss what you actually accomplished with all those small steps.

So here’s a look back at the writing and speaking I did last year, along with the best things I learned along the way.

What went well

I started to write here on January 10, with Estimates Are Not a Goal, They’re a Communication Tool. In 2014, I posted 53 articles, more than one per week! Starting in April, I’ve posted an article every Tuesday. Creating that kind of schedule makes writing and planning so much easier.

I tried to start writing a few times before, and always gave up for two reasons: I ran out of topics, and it didn’t seem like anyone cared. Two things made it different this time:

  • I started solving problems instead of just writing. I learned this from Amy Hoy and Alex Hillman’s (completely awesome) 30x500 course: If you focus on helping people with the problems they have, you’ll never run out of things to write about.

  • I started telling people when I posted. I’ve always been nervous about sharing my own stuff. But when you just start out, how are people going to know about what you write?

    If you’re posting helpful things and participating in the discussion around it, people are happy to learn from you. So tell them about it!

These were my most popular articles from last year:

Take a look if you missed them the first time around!

A few articles in, I started an email list. Through the end of 2014, it’s grown to 1,670 subscribers! I send emails to the list every Friday, with 41 sent so far. They range from deeper looks on some of the articles I posted, to answering questions, to exclusive articles about the problems I hear about from my subscribers. If you haven’t joined already, sign up here. I’d love to hear from you.

One problem with the email list is that once you miss an email, you miss it. So this year, I’m going to find a way to get the best emails you’ve missed to you.

I also wrote a book about learning Rails without getting overwhelmed, and started to pre-sell it. You can get early access here. It’s 25% off until the final release, and you’ll get the final update once it ships. More than 300 people have received early access, and it’s already helped a lot of them get past the tutorial stage so they could start building their own Rails apps.

Finally, here are a few other things I did in 2014:

What didn’t go so well

I create schedules and due dates for myself that are, in retrospect, totally insane.

Once it’s clear that you’re not going to hit a goal, you start beating yourself up. You start thinking, maybe if you worked a little harder, maybe if you stayed up a little later, maybe if you were just plain better at what you were doing, you wouldn’t be in the mess you’re in. But it’s a self-imposed mess! It makes no sense.

With aggressive goals, I’ve done a lot of stuff. But too-aggressive goals and too-high expectations have hurt more than they’ve helped. I have to find more of a balance.

Lessons learned

The best things I’ve learned this year about creating things:

  • Schedules and habits are better than motivation.

    I credit all of the work I’ve done here to setting up the right habits and schedules.

    You still need motivation to get those habits started. But that motivation is better spent setting up good systems and habits, instead of spending the motivation on the work itself. Because motivation eventually fades, but a good habit sticks around for a lot longer.

  • Rough first drafts make the creation process faster.

    The first drafts of my articles are unreadable. But rough drafts are easy to improve. I don’t have to keep what I’m thinking about in my head, I can put something down and improve it piece by piece.

  • Sometimes you have to close your eyes and hit submit.

    Some of the articles I’m most proud of are articles I came close to not posting. It’s nerve-wracking to put something you created out in public, especially as you’re learning. When that happens, you just have to git push and walk away from the computer for a little while. What you made is never as bad as it seems right before you publish.

What about you? What were your biggest accomplishments last year? What did you learn? And what are your plans for 2015?

You’re excited about the app you’ve made. There’s just one problem — you don’t have any tests. You wanted to write it using Test-Driven Development, but you didn’t quite know where to start. So you’re stuck. Where do you go from here? How do you get from an app with no tests, to writing your apps using TDD?

Test the code you already have

You have a bunch of code with no tests. But that doesn’t mean you can’t write your tests now, against the existing code. So start testing the code you already have, to make sure your code works the way you expect it to work.

It’s not TDD. But testing existing code will help you learn TDD:

  • You practice thinking about edge cases and error conditions.

    To write tests without spending years testing every single possible input, you have to think about where the code is most likely to break. If the method you’re testing takes a string, what happens if you pass it a symbol? What happens if you pass it nil? And if you’re testing a function that divides numbers, you’d better test it with 0. But you probably don’t need to test it with 1 and 2.

    After you write enough tests, you’ll start to predict where your methods are most likely to break. And once you start TDDing, you can use this skill to write robust tests that force your code to better handle your edge cases.

  • You practice writing well-structured tests.

    When you write tests after-the-fact, you can try different patterns for structuring those tests. The code you’re testing is already there, so you can focus on your test, and how it’s written. And once you learn some good patterns, you’ll write better tests when you don’t have the code to lean on.

  • You discover the things that make code hard to test.

    As you write more tests, you’ll begin to sense which areas of your system will be the hardest to test. When you notice those areas, you can highlight them as places that need refactoring. Even better, you’ll start to write more testable code the first time.

    Once you know what easy-to-test code looks like, you can TDD easy-to-test APIs with that knowledge. And that will help you build your apps faster.

Ease into TDD

You can use test-after to build skills that help you learn TDD. But you still want to TDD pieces of your app eventually. And there’s a simple way to ease into TDD, and still lean on existing code: write regression tests.

Regression tests keep you from breaking code you’ve already fixed. The idea is pretty simple. Whenever you hear about a bug, instead of clicking around in the browser to reproduce it:

  • Write a failing test to reproduce the bug.
  • Run the tests, and make sure they fail (because that bug still exists).
  • Fix the bug in the simplest way possible.
  • Run the tests, and make sure they pass.
  • Refactor your fix, if necessary.

This is a lot easier than TDDing a new system from scratch, because you’re just test-driving a change to code that’s already there. You build the habit of “Red, Green, Refactor” that is the essential loop of TDD. And from here, TDD is a shorter step away than trying to go straight to TDD from no tests.

From nothing to TDD

An app without tests isn’t a bad place to start. When you test existing code, you’ll learn a lot of what you need to write good TDD tests. Test-after is easier than TDD at first, because you don’t have to imagine APIs that you don’t know how to design yet. And once you decide to bring TDD into your app, you can ease into it with regression tests.

So, if you don’t know how to TDD the system you’re imagining, keep writing tests. Even if you have to write the code first.

The Rails community is full of conflicting advice. Do you use Minitest or RSpec? Do your plain Ruby objects go in app/models, app/classes, or lib/?

If more experienced devs constantly argue about these things, what hope do you have of deciding which advice to follow?

What’s the secret?

Here’s the secret: just pick one side, and follow it.

It seems like a cop-out. But these are arguments over small differences in philosophy. The stronger the argument, the less important the choice actually is. Especially in small apps, like the ones you’ll start with.

Experienced devs got that experience somehow. They learned, they wrote code, they shipped apps. Whichever choice they made didn’t sabotage their progress. So following that choice won’t sabotage yours.

I’m a big fan of Minitest, and don’t use RSpec when I can avoid it. I’ve written a ton about why I prefer Minitest over RSpec. But if you decide that RSpec is the framework you should be using, you’re not going to end up with an unusable app. If you end up switching to Minitest later, you won’t be starting all over again. Most of the testing skills you learned will carry right over. So just pick one. Flip a coin if you have to. Because either choice is better than none.

It’s easier to change than you think

The right choice for you depends on your personality, your environment, your team, and your past experiences. These things will change over the course of your career. So if you spent days learning all the background and the nuances of each side and finally made the perfect choice, that choice may eventually change.

You’ll make some bad choices. Some of the best developers I know wrote entire gems based on philosophies that they now argue against. In code, few opinions last forever.

So don’t agonize over picking the right side in a Rails argument that you don’t totally understand yet. If it doesn’t work out, you can always change your mind.

After you finish the Rails tutorials and start your own app, things get confusing. Like where does your non-CRUD, general logic go? How does grabbing followers from Twitter fit into MVC? Ask two people, and you get four answers. Or your thread devolves into a bunch of smart people insulting each other for hours, while you beat your head against the desk. Either way, you’re left with a headache.

You can’t build the app you’ve been dreaming of without some general, non-Rails logic. So where do you put your code, and still keep things simple?

The easy place to start

When I have logic that feels related to an existing ActiveRecord model, I’ll start by putting it into that model. For example, if I had a Game model and I wanted to import a bunch of games from CSV files, I’d put that method right onto the Game class:

app/models/game.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Game < ActiveRecord::Base
  def self.parse_from_csv(csv_string)
    games = []
    CSV.parse(csv_string, quote_char: "'") do |row|
      games << Game.from_csv_row(row) if (row[0] == 'G')
    end
    games
  end

  def self.from_csv_row(row)
    Game.new({
      dgs_game_id: row[1],
      opponent_name: row[2],
      created_at: row[4],
      updated_at: row[4],
    })
  end
end

You have all the information your methods need right at hand. It’s easily testable. And it’s probably where a new contributor would look for that logic first.

But if you keep adding and changing that model, it’ll get big and complicated. Different parts of the model will interact in strange ways. The more you change it, the harder it will be to change.

In that case, you’d probably want to refactor that code out to a non-ActiveRecord model.

Non-ActiveRecord models

Just because it’s in a Rails app, doesn’t mean it has to inherit from Active/Action-whatever.

You can write your own Ruby code, in plain Ruby objects, and use them in your Rails app. These objects can still be called models, because they’re modeling part of your problem. They just don’t have an ActiveRecord database storing their data.

The next time I worked on that game CSV parser, the Game class was getting a little too big. So I moved the parser logic into its own GameCSVParser class.

The whole commit is here, but this is what the non-ActiveRecord class looks like:

app/models/game_csv_parser.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class GameCSVParser

  def initialize(csv_string)
    @rows = CSV.parse(csv_string, quote_char: "'")
  end

  def games
    game_rows.map { |row| Game.new(game_attributes(row)) }
  end

  private

  def game_rows
    @rows.select { |row| is_game_row?(row) }
  end

  def game_attributes(row)
    {
      dgs_game_id: row[1],
      opponent_name: row[2],
      created_at: row[4],
      updated_at: row[4],
    }
  end

  def is_game_row?(row)
    row[0] == 'G'
  end
end

I’ll go right to creating a new plain Ruby object if the logic I’m adding doesn’t feel related to any specific ActiveRecord model. Or if the code seems like it should be a part of a thing that doesn’t exist yet in the app. Otherwise, they mostly pop up through refactoring.

With plain Ruby objects, you can write anything. But knowing you can write anything doesn’t help you with direction. What methods do you need? How will all your new objects interact?

A lot of Rails apps use the same categories of plain Ruby objects. These categories are patterns you can follow to write code that other developers would recognize. You might have heard of a few of them already.

Service objects, presenters, and jobs

There’s nothing special about service objects, presenters, and jobs. They’re just plain Ruby objects that act in a particular recognizable way.

For example, a Resque job is a plain Ruby class that has a perform method and a @queue:

app/workers/fetch_games_for_player.rb
1
2
3
4
5
6
7
8
class FetchGamesForPlayer
  @queue = :default

  def self.perform(player_id)
    player = Player.scoped_by_id(player_id).ready_for_fetching.first
    player && player.fetch_new_games!
  end
end

perform is called when the job is run.

A presenter is a plain Ruby object with code that only makes sense inside a view:

app/presenters/user_presenter.rb
1
2
3
4
5
class UserPresenter
  def show_related_users?
    @user.related.count > 3
  end
end

It might also include Rails’ view helpers, or take a few different objects and treat them as one unified object for the view’s convenience.

A service object is a plain Ruby object that represents a process you want to perform. For example, writing a comment on a post could:

  1. Leave the comment.
  2. Send a notification mail to the author of the post.

A service object could do both, and keep that logic out of your controller.

There’s a great take on service objects here. It’s full of examples.

For simple processes, I don’t bother with service objects. But if the controller starts getting too heavy, they’re a good place to put that extra logic.

You can use these patterns to organize your own business logic. They’re just plain Ruby objects, but they’re Ruby objects that share a certain flavor, that have a name, and that you can talk to other developers about.

Where do you start?

There are a lot of different places your non-Rails business logic could go. It might be hard to choose. So here’s what I do:

  1. If the logic is mostly related to an existing class, even if it’s an ActiveRecord model, I put it in that class.
  2. If it doesn’t fit an existing class, I create a new plain Ruby class to hold the logic.
  3. If it feels like the logic should be part of something that doesn’t exist yet, I create a new plain Ruby class for it.
  4. If I come back to the code later, and the model is getting too complicated, or the code doesn’t make sense in that model anymore, I’ll refactor it into its own plain Ruby class.
  5. If the code only makes sense in a view, I’ll add it to a helper or create a presenter.
  6. If the code doesn’t need to run during an HTTP request, or has to run in the background, it goes in a job.
  7. If I’m juggling several different models or stages of a process, and it’s making the controller too hard to understand, I’ll put it into a service object.

How about you? Where does your code go? And do you have any patterns besides these that you’ve found helpful? Leave a comment and let me know.

And if you don’t have a process yet, try mine. See how it fits you. There’s no perfect way to write code, but when you get stuck, a process like this will help you get started.

(This post is based off of one I sent to my list a few months ago. To read more like it every week, join up here!)

In Using a Little Bit of Convenience to Save Your Programming Flow, I talked about how the simplest abstractions can make your codebase more fun to work with.

But what about when you have a bad abstraction?

You dig out the API reference every time to understand what that method that clearly should have been named leaderboard is actually called. You try to refactor around it, but one part of the code hands you structured data and the other part gives you raw lines of text. And every time you touch the code, something breaks, and you blow the rest of your day trying to debug it. Because you tweaked position, but didn’t update score at the same time.

So how do you build good abstractions, while staying far away from the bad?

Using good metaphors to write clearer code

Good abstractions come from good metaphors.

A metaphor describes something in your system by relating it to a concept that’s already in your head. For example:

  • Many command lines use the metaphor of a pipe.

    You put data in one end of the pipe, and it comes out the other end. Easy to imagine and easy to use.

  • In concurrent programming, a lock is a good metaphor.

    Without knowing anything about how locks work internally, you know that when a resource is locked up, you can’t use it. You have to wait until it’s unlocked.

  • In Object-Oriented Programming, message passing triggers a specific mental image: An object passes another object a message. The second object can do something with the message or pass it to someone else.

    You don’t need to know anything about the implementation in order to get the idea (and it makes things like the proxy pattern much easier to understand).

A good metaphor is critical. If you see an abstraction based on a good metaphor, you’ll rarely have to read the documentation or go source diving to understand the code. The better the metaphors you come up with, the easier it will be to describe your system to someone else, and the easier it’ll be to work on your system later on.

How do you come up with good metaphors?

A good metaphor is specific, self-contained, and creates a vivid mental image of the thing it describes. It doesn’t leave room for misunderstanding, and it matches the code’s behavior as closely as possible. If someone makes assumptions about the code based on their understanding of the metaphor, those assumptions should be valid.

Coming up with good metaphors is hard. You’ll often go through a few leaky metaphors before you can find the right one, and that’s OK! It will get easier with practice. But here’s a process that’s helped me:

Think about what a bit of code does, without thinking about how it does it. What image comes up in your mind? How does other code use it? If you had to describe it in a word or two, which words would you use? Write that down.

Then, think about that image. Does it fit how that code works? Are there edge cases that leak through the metaphor? Is there anything besides the metaphor a new contributor would have to understand before they could use the code? When you think about your metaphor, do you get a clear picture in your mind, or is it fuzzy?

These are indicators that your metaphor doesn’t match your code closely enough. But the closer you are to a good metaphor, the less of a leap you’ll have to make to get there.

Finally, use it in a sentence. “This code is a (something), because…” Could you explain your metaphor to someone without a technical background, and have them understand what it does? The less background someone needs to understand the metaphor, the more helpful your metaphor will be.

If you’re close, but not quite there, take a look in a thesaurus. Subtle differences between similar words can mean the difference between an OK metaphor and a perfect one.

Metaphors and OOP

Object-Oriented Programming is fundamentally based on metaphor. That’s where it gets its power! Well-designed classes don’t just hide their implementations from other classes, they hide their implementations from you.

With a great metaphor, you should be able to tell just from the class name how to use your objects. If your classes are named well, and they cause the right images to appear in your mind, you shouldn’t have to dive into their implementation to understand them.

So now, I’d like you to think about the classes in the last code you worked with. Can you think of any that have particularly clear metaphors? Which classes do you feel like you work with, rather than work against? How often do you go digging into them to figure out how to use them?

I’d love to hear what you find out. Leave a comment, and let me know.

To become a great software developer quickly, you have to consciously think about the code you’re writing and the processes you’re following. And I can help you.

Download my free 32-page sample of Practicing Rails, and learn how to try ideas like this on a small scale, without getting overwhelmed.

Generally, global variables are bad. But sometimes, a global in the right place can make your code a lot simpler.

In Rails, you might set data once during a request, but use it in every layer of your app. Which user is making the request? What permissions do they have? Which database connection should be used?

This information should be available throughout your code, so passing it everywhere would just be a bunch of noise. But using a Ruby global variable, or setting a class variable, will cause its own problems. Multiple threads could overwrite it, and you’d end up with a huge mess, and maybe unrecoverably bad data.

You need something that’s global, but for just your request.

The plain Ruby way

Usually, you’ll see this handled with Thread.current[]. It looks like this:

1
Thread.current[:current_user] = user

This is simple enough. It’s a decent solution. But it has two big drawbacks:

  1. Someone could accidentally overwrite your data.

    What if two people picked the same key? You’d stomp on each other’s data. And if you’re storing something like users, that would be seriously bad. In your own apps, this probably won’t be a problem. But if you’re writing gems, or your Rails app is big and messy, it’s something you need to think about.

  2. It’s not well-structured.

    Thread.current[] is just a big bag of data. You can’t easily document it. If you want to know what you can take out of it, you have to search for what you put into it.

    Of course, if you’re putting away enough global data that this is a problem, you have more to worry about than Thread.current[]’s lack of structure. But it’s still a point to keep in mind.

So, is there a better solution than Thread.current[]? As you might imagine, Rails itself manages a lot of request-local data. And the box of goodies that is ActiveSupport has a different pattern to follow.

The Rails way

If you dig through ActiveSupport, you might run into ActiveSupport::PerThreadRegistry. You can probably tell from the name that this is exactly what we’re looking for.

With ActiveSupport::PerThreadRegistry, the earlier example would look like this:

1
2
3
4
5
6
7
8
class RequestRegistry
  extend ActiveSupport::PerThreadRegistry

  attr_accessor :current_user, :current_permissions
end

RequestRegistry.current_user = user
RequestRegistry.current_user # => user

It’s a tiny bit more work. But with ActiveSupport::PerThreadRegistry:

  • You have a place to put documentation. Anyone looking at your class will know exactly what global data you’re expecting, and what global data they can use.

  • You get an API that looks good. In fact, it looks like you’re just setting class variables. That’s probably what you’d be doing if you didn’t have to worry about threads.

  • Any data you set on the class will be namespaced. You don’t have to worry about RequestRegistry.current_user colliding with PhoneNumberApiRegistry.current_user, they’re treated totally separately.

Rails uses ActiveSupport::PerThreadRegistry internally, for things like ActiveRecord connection handling, storing EXPLAIN queries, and ActiveSupport::Notifications. So if you’re looking for more good examples of the power of PerThreadRegistry, Rails itself is a great place to start.

When threads and requests don’t match

For some Rails servers, a single thread handles multiple requests. This means anything you put into a PerThreadRegistry will stick around for the next request. This is probably not what you want.

You could do what Rails does, and clean up the things you put into the PerThreadRegistry after you’re done with them. Depending on what you put in there, you might be doing this anyway.

In the comments, MattB points to an easier solution: request_store. It acts like Thread.current[], but clears itself after each request.

This leaves the door wide open for someone to create a PerRequestRegistry, which combines the safety of request_store with the API of PerThreadRegistry. If someone’s done this already, I’d love to hear about it!

Try it out

Global data can be bad. Too many globals can quickly lead to unmaintainable code.

But if it makes sense to keep data in a central place for an entire request, go beyond Thread.current[]. Give ActiveSupport::PerThreadRegistry or request_store a shot. If nothing else, it’ll make dealing with global data a little less risky.

I love each, but I have a problem with it. What happens when you have an empty collection?

If you call [].each, nothing will happen and [] will be returned. Sometimes, that’s what you want. But more often, especially when you’re building UI, you’ll want to handle empty lists in a special way. For example, I usually want to show a different message when I don’t have any data.

But since [].each returns [], not nil, you’re stuck writing something like this:

app/views/users/index.html.erb
1
2
3
4
5
6
7
<% if @users.empty? -%>
  <p>You don't have any users. <%= link_to "Add one!", new_user_path %></p>
<% else -%>
  <% @users.each do |user| -%>
      <p><%= user.name %></p>
  <% end -%>
<% end -%>

That works, but it feels awkward. I’d rather say, “Do this thing to each item in this collection, unless there’s nothing in it, in which case do this other thing entirely.” I want something like each, with an “or else.”

Rendering Rails Collections

Inside your views, Rails can help. It’s great at rendering collections of things:

app/views/users/index.html.erb
1
<%= render @users %>
app/views/users/_user.html.erb
1
<p><%= user.name %></p>

When render is passed @users, it renders _user.html.erb once for each user inside @users. You can skip the each entirely.

As a bonus, if @users is empty, render returns nil, just like we want! So you can write this, and get the same output as the original version:

app/views/users/index.html.erb
1
<%= render(@users) || render('empty') %>
app/views/users/_user.html.erb
1
<p><%= user.name %></p>
app/views/users/_empty.html.erb
1
<p>You don't have any users. <%= link_to "Add one!", new_user_path %></p>

It’s a lot more direct, once you understand Rails’ conventions.

What about outside of a view?

If you follow Rails, render with a collection is a fast, powerful way to render collections of objects.

But sometimes you won’t want to deal with an extra partial to render each item. Or render won’t support your design. Or you won’t even be inside a view.

The best solution I’ve found so far is presence.

list.presence is the same as:

1
2
3
4
5
if list.present?
  list
else
  nil
end

That is, you’ll get the list back if it has anything in it, and nil if it’s empty.

With presence, you could write:

1
2
3
@users.each do |user|
  puts user.name
end.presence || puts("You don't have any users.")

This prints each name if there are users in @users, or You don't have any users otherwise.

Still, I could do without the presence. It feels like a hack, because it is one. If it was supported, something like this might be better:

1
2
3
@users.each do |user|
  puts user.name
end || puts("You don't have any users.")

or the Smalltalk-ish:

1
2
3
@users.each(if_empty: lambda { puts "You don't have any users." }) do |user|
  puts user.name
end

or even (gasp!):

1
2
3
4
5
for user in @users
  puts user.name
else
  puts "You don't have any users."
end

For now, though, I usually just go with presence or the basic if blank?; else each pattern.

Which do you think is better? How do you handle empty lists? Have you found a better way? If so, tell me about it!