Memoization is a technique you can use to speed up your accessor methods. It caches the results of methods that do time-consuming work, work that only needs to be done once. In Rails, you see memoization used so often that it even included a module that would memoize methods for you.

Later, this was controversially removed in favor of just using the really common memoization pattern I’ll talk about first. But as you’ll see, there are some places where this basic pattern just doesn’t work right. So we’ll also look at more advanced memoization patterns, and learn some neat things about Ruby in the process!

Super basic memoization

You’ll see this memoization pattern all the time in Ruby:

app/models/order.rb
1
2
3
4
5
6
class User < ActiveRecord::Base
  def twitter_followers
    # assuming twitter_user.followers makes a network call
    @twitter_followers ||= twitter_user.followers
  end
end

The ||= more or less translates to @twitter_followers = @twitter_followers || twitter_user.followers. That means that you’ll only make the network call the first time you call twitter_followers, and future calls will just return the value of the instance variable @twitter_followers.

Multi-line memoization

Sometimes, slow code won’t fit on one line without doing terrible things to it. There are a few ways to extend the basic pattern to work with multiple lines of code, but this is my favorite:

app/models/order.rb
1
2
3
4
5
6
7
8
9
class User < ActiveRecord::Base
  def main_address
    @main_address ||= begin
      maybe_main_address = home_address if prefers_home_address?
      maybe_main_address = work_address unless maybe_main_address
      maybe_main_address = addresses.first unless maybe_main_address
    end
  end
end

The begin...end creates a block of code in Ruby that can be treated as a single thing, kind of like {...} in C-style languages. That’s why ||= works just as well here as it did before.

What about nil?

But these memoization patterns have a nasty, hidden problem. In the first example, what if the user didn’t have a twitter account, and the twitter followers API returned nil? In the second, what if the user didn’t have any addresses, and the block returned nil?

Every single time we’d call the method, the instance variable would be nil, and we’d perform the expensive fetches again.

So, ||= is probably not the right way to go. Instead, we have to differentiate between nil and undefined:

app/models/order.rb
1
2
3
4
5
6
class User < ActiveRecord::Base
  def twitter_followers
    return @twitter_followers if defined? @twitter_followers
    @twitter_followers = twitter_user.followers
  end
end
app/models/order.rb
1
2
3
4
5
6
7
8
9
10
class User < ActiveRecord::Base
  def main_address
    return @main_address if defined? @main_address
    @main_address = begin
      main_address = home_address if prefers_home_address?
      main_address ||= work_address
      main_address ||= addresses.first # some semi-sensible default
    end
  end
end

Unfortunately, this is a little uglier, but it works with nil, false, and everything else. (To handle the nil case, you could also use Null Objects and empty arrays to avoid this problem. One more reason to avoid nil!)

And what about parameters?

We have some memoization patterns that work well for simple accessors. But what if you want to memoize a method that takes parameters, like this one?

app/models/city.rb
1
2
3
4
5
class City < ActiveRecord::Base
  def self.top_cities(order_by)
    where(top_city: true).order(order_by).to_a
  end
end

It turns out that Ruby’s Hash has an initalizer that works perfectly for this situation. You can call Hash.new with a block:

1
Hash.new {|h, key| h[key] = some_calculated_value }

Then, every time you try to access a key in the hash that hasn’t been set, it’ll execute the block. And it’ll pass the hash itself along with with the key you tried to access into the block.

So, if you wanted to memoize this method, you could do something like:

app/models/city.rb
1
2
3
4
5
6
7
8
class City < ActiveRecord::Base
  def self.top_cities(order_by)
    @top_cities ||= Hash.new do |h, key|
      h[key] = where(top_city: true).order(key).to_a
    end
    @top_cities[order_by]
  end
end

And no matter what you pass into order_by, the correct result will get memoized. Since the block is only called when the key doesn’t exist, you don’t have to worry about the result of the block being nil or false.

Amazingly, Hash works just fine with keys that are actually arrays:

1
2
3
h = {}
h[["a", "b"]] = "c"
h[["a", "b"]] # => "c"

So you can use this pattern in methods with any number of parameters!

Why go through all this trouble?

Of course, if you start adding these memoization patterns to a lot of methods, your code will get pretty unreadable pretty quickly. Your methods will be all ceremony and no substance.

So if you’re working on an app that needs a lot of memoization, you might want to use a gem that handles memoization for you with a nice, friendly API. Memoist seems to be a good one, and pretty similar to what Rails used to have. (Or, with your newfound memoization knowledge, you could even try building one yourself).

But it’s always interesting to investigate patterns like this, see how they’re put together, where they work, and where the sharp edges are. And you can learn some neat things about some lesser-known Ruby features while you explore.

(I sent this post to my list a while back. If you enjoy it, and want to read more like it, you should sign up!)

The Rails ecosystem moves fast, way too fast for print. If you’re like me, you want to learn the most recent version of your frameworks and gems. But the best resources are often a few versions behind.

These resources are still useful, though. There’s nothing like a well-edited book, screencast, or tutorial to learn a library’s API, philosophy, and structure.

For example, if the best Rails books only describe Rails 4.0, you should still start from there. When you’re learning a new gem or framework, it’s important to learn why it’s designed a certain way and how all the pieces fit together. That’s hard to get from reference documentation and blog posts, but easy to get from books.

But once you build experience with an older version of a gem, how do you get caught up?

Catching up with changes

Most popular gems have CHANGELOG files in their git repository, like bundler: https://github.com/bundler/bundler/blob/master/CHANGELOG.md. These are a great way to catch up on the big changes from version-to-version. Usually, they’re just a short summary of each major change. But they give you a starting point, so you can do more research on interesting changes.

Many changelogs reference bug numbers on GitHub. If you see an entry in a changelog that has a bug number attached, you can find the bug in the project’s Issues to understand what changed, how it changed, and why it changed.

So, how do you find changelogs? Usually, I just search google for bundler github (if I’m looking for the bundler changelog), and they’re usually on the first GitHub page you see.

If there isn’t a changelog, you can also look at the project’s README or the project’s wiki on GitHub. But since those aren’t designed to help you catch up from version-to-version, they usually take longer to go through.

Using up-to-date reference documentation

When you’re working with gems, you’ll also need to keep up-to-date reference documentation around. That way, you can look up API usage and see examples while you’re writing your own apps.

You can find API documentation for any version of any gem at rdoc.info. But for even faster doc lookup, you should check out Dash or Zeal.

I use Dash, so when I need to look up API docs, I hit option-Space, start typing, and all my gem documentation shows up instantly. It’s a change in my workflow that’s paid for itself many times over.

A few tips on Rails, specifically

Rails is a large project, and the Rails contributors do a great job of maintaining changelogs and documentation.

The Rails guides are good, and they’re also built from the same git repository as Rails, so they’re always up to date.

If you’re trying to catch up to the newest version of Rails, the release notes are the best place to start. For example, here are the release notes for Rails 4.1: http://guides.rubyonrails.org/4_1_release_notes.html

The process I usually take

Putting it all together, this is what I do when I want to get completely up to date on a new library:

  1. Read a book, tutorial, or documentation about the gem. I usually try to find the newest resource I can. While I’m reading, I build an app, practice, etc. on whichever version I’m learning.
  2. Find the project on GitHub.
  3. Read the project’s changelog, readme, or wiki to take me from the version I know to the most recent version.
  4. If I’m interested in a specific change, google around for it or go through the project’s issues to learn more.
  5. Upgrade to the newest version of the gem.
  6. Install the new gem’s documentation in Dash.
  7. Write code with it!

I don’t do all those steps all the time. But that’s generally the order I go in, and this kind of process is helpful to keep around in case I get stuck on a step or don’t know where to go next.

What if you can’t find a changelog or can’t make sense of the docs?

Sometimes you just won’t be able to get the information you need out of the gem’s GitHub repo or API documentation. When that happens, you’ll have to dive into the code and start reading it.

Surprisingly, reading code is not like reading a book. Instead of reading files from beginning to end, you have to explore the code. This is more of an art than a science, but it’s an important skill to learn. So, I’ll probably have more to say about it later!

What tricks have you learned for getting caught up on gem changes? How do you stay up to date?

API docs will tell you how to use an API. But when things go wrong, you can be left on your own. Error messages are often incomplete, misleading, or unhelpful. And what are you supposed to do with NoMethodError: undefined method '[]' for nil:NilClass, anyway?

When you learn an API, framework, or library, you can’t just learn how to use it when things go well. You also need to figure out what to do when it hands an error back.

How do you break code?

There’s an easy way to learn what to do when an API you’re using breaks. Break it yourself!

For example, I’ll:

  • Pass data of the wrong type. You could pass a symbol instead of a String, a String instead of an Array, a Hash instead of an Array, things like that.

  • Pass incomplete data. You can pass nil, hashes without some fields filled out, and objects that have just been initialized.

  • If the API requires network access, disconnect from WiFi or pull the network cable. Does it just time out, or does it tell you which service it couldn’t reach?

  • If the API allows you to pass in a block, throw exceptions inside the block, or return data of the wrong type.

A great API will tell you what you did wrong. An excellent API will tell you how to fix it. But most often, you’ll run into a Ruby NoMethodError, unexpected nils, or, worse, getting totally bizarre return values back.

Why break code?

This isn’t all bad. If you’re playing with an open-source gem, you can take time to understand where that unexpected behavior came from and why it happened. You can debug and read a little bit of code to learn a lot about how the library or API works.

Once you discover where the NoMethodError is coming from, you can go a step further. You can fix the error message for the next person to run into this problem for real! Little error message fixes make great open-source contributions and easy pull requests. And they make the entire Ruby ecosystem a little better for everyone else.

Even if it’s a closed-source REST API, you can still get something out of this exercise. After you see the different errors you’ll get from the API, you’ll have an easier time fixing the problem when you run into an error for real.

Once you become more comfortable seeing and fixing errors in the code around you, you’ll see broken code as a puzzle to be solved. You won’t automatically recoil when you see an exception and backtrace dumped to the screen. Instead, you’ll see them as an opportunity to learn more about the system you’re working with.

Finally, you’ll know that when code breaks, you’ll be able to put it back together again. You’ll improve your confidence in trying new libraries and APIs. And that boldness will increase your rate of learning new things.

There’s a lot of good, free Rails information around. But as you improve your development skills, it can be hard to find knowledge that’s useful to you.

If it’s too basic, you’ll just read about things you already know. Too advanced, and your eyes glaze over and your brain shuts off. Three paragraphs in, and you don’t even remember what your name is anymore!

You can’t just type “intermediate-level Rails blogs” into Google and hope some good sites pop out. To get the information you’re looking for, you’re going to have to do some digging.

Finding & filtering

First, look wide and shallow. When you use social sites like reddit, twitter, and Stack Overflow, click on every link that looks interesting to you. Skim an article or two on each site, and if the site seems interesting, keep it around.

Next, you can filter and curate. You should build a place where posts find you. If you use an RSS reader (I use Feedbin), subscribe to the site. If you prefer email, sign up to receive email updates. You just want to be sure you see new articles as they come in.

If you skip more than a few articles from a site, unsubscribe. Eventually, you’ll find a small group of people that you enjoy hearing from, who post things you’re interested in, and that you can learn something from.

They’ll grow along with you, and the articles will get more advanced at a rate you can handle. Plus, they’ll often link to the people who they find insightful, which is a much faster way of finding people to learn from.

It’ll take a while to build this group. But you’ll get a little bit more of a benefit from every site you find.

Learn serendipitously

This doesn’t completely solve the “at your level” problem. But I have a little secret: Finding people at just the right level for you isn’t actually all that important. Many of my favorite writers write things that are way more basic or way more advanced than my current skill level.

How does this work?

When something is too basic, you can think of it as a way to review your fundamentals. The first time you learned something, you might have formed bad habits or internalized some ideas incorrectly. When you review basic information you get a second chance to think about and improve those things.

I had this happen when I read Eloquent Ruby as part of the RubyRogues’ book club.

The first few chapters were really basic—I almost stopped reading it, because I didn’t think I’d learn anything interesting. But I went through it, I focused on it as if I was learning Ruby for the first time. And I came out of it with some Ruby tricks and conventions that I had totally forgotten about.

What about articles that are too advanced? You can get a lot out of those, too:

  • You get introduced to more jargon and patterns. Even if you can’t understand what the terms mean, you can search for the ones that sound interesting. You might find some better descriptions, or other good sites to subscribe to!

  • The more often you see an advanced idea, the less you’ll be intimidated by it when you have to learn it for real. It’ll also be easier to learn, because you will have already learned a little about it from seeing it in context.

Just for fun, I subscribe to some CS and math blogs that assume my math skills are way beyond where they actually are. Sometimes I only get a paragraph or two into the article before I give up. But the paragraphs I do read are some of the most interesting and insightful things that come through my feed reader.

You do have to come into advanced blogs and resources with the right mindset. You can’t let them intimidate or overwhelm you. You have to remember that it’s not a reflection on you if you don’t get it. And getting anything out of an article that’s beyond your skill level is a big accomplishment.

So, find some interesting articles. Follow some blogs. If they stay interesting, keep them around. Even if they’re not quite at your current skill level.

Last week, I wrote about methods with consistent return values, and how they’ll make your code simpler. But how does that happen? How can the right refactoring make your code easier to work with? What makes good abstraction good, and bad abstraction bad? I mean, you can’t just blindly rearrange your code and end up with quality software.

If you can learn why these techniques work, you’ll be better able to understand where your code needs help. And you’ll be able to use the right tools where they matter most.

What’s the hardest problem in software development?

“There are only two hard problems in Computer Science: cache invalidation and naming things.”

— Phil Karlton

Sure, that quote is everywhere. But there’s actually a harder problem in real-world software development: managing complexity. Complexity in software development has a few different definitions. At its core, though, the complexity of a program is the amount of stuff you have to keep in the front of your mind while you’re working on it.


As you write more software, you’ll be able to keep more stuff in your head at once. But even the best devs have limits. If your project is too complex, you’ll overload what your mind can handle, and you’ll start forgetting about different areas of your program. And the worst bugs show up when you make a change in one part of your project without realizing how the change will affect a totally different part of the same project.

Simplifying through assumptions

If you had photographic memory, and could remember how all the different parts of your program fit together, you’d have a lot fewer bugs, right? Memory can be hard to build. But you can get a lot of the same benefits from being able to focus on one thing at a time.

Many best practices in software development are about reducing the number of things you have to keep in your mind at once. A few examples:

  • Consistency in your return values means you only have to think about how to deal with one kind of data, instead of different kinds of data in different situations.

  • Abstraction is a way to hide code behind a simpler interface. With good abstractions, you can just think about how the abstraction should act, and you don’t have to worry about how it works on the inside.

  • Testing can keep you from accidentally breaking code in one area while you’re working in another. You can assume that anything you accidentally break will be caught. This means you can focus just on the code you’re working on, and fix anything you break later.

  • Test-Driven Development will help you write code that will work the way you expected it to work. You can concentrate on writing the simplest possible implementation of your method that passes the tests. If it’s too simple and doesn’t actually work as you’d expect, your tests will catch it.

When you use these techniques in the right way, you can make some good assumptions. With these assumptions, you don’t have to keep anywhere near as much in the front of your mind. You can write better, more reliable code, because you don’t have to worry about the nearly infinite consequences your code could have on the system.

On the other hand, using these techniques in the wrong place will hide code that you actually need to see. By hiding the code, the assumptions you make will sometimes be wrong. This makes it even more likely that you’ll end up breaking it!

That’s why bad abstractions are worse than no abstraction at all, and why you can’t just throw “extract method” at code until it magically becomes good.

Right refactoring, right place

Clear code is code that’s up-front about what it’s doing. Showing what you’ll need to know is as important as hiding what you don’t need to know. When you perform a refactoring, or use a software best practice, you should think about the code you end up with, and how well it’s helped you reduce complexity. Are you helping the rest of the code make good, simplifying assumptions? Or are you burying essential knowledge behind a too-simple interface?

I originally learned a lot of these ideas from Confident Ruby, one of my favorite Ruby books. If you like this post, you should buy it and read the entire thing. There’s so much good stuff in there.

Your current_user method returns a User, except when there is no user and it returns nil. A search method returns an Array of results, unless there’s only one result, and it returns just that result instead. Seems reasonable, right? Maybe even convenient!

But soon, these decisions will bury your code under a mountain of if statements. Maybe it’s a bunch of if kind_of? sprinkled all over. Or maybe you feel like you have to check for nil everywhere. Or worse, NoMethodErrors start showing up whenever you ship a new feature. Guess it’s time for another hotfix!

There is a way to prevent this, though, and all it takes is a little thoughtfulness.

The Robustness Principle

There’s a principle in computing that says,

Be conservative in what you do, be liberal in what you accept from others.

You can apply this principle to your Ruby methods. The methods you write should accept reasonable input, and should return consistent output.

Focusing on the last part: When someone calls a method you wrote, they should know exactly what that method will return.

Be thoughtful about your output

Take a look at Rails 2.1’s implementation of ActiveRecord::Errors#on:

1
2
3
4
5
6
7
# File activerecord/lib/active_record/validations.rb, line 212
def on(attribute)
  attribute = attribute.to_s
  return nil unless @errors.has_key?(attribute)
  errors = @errors[attribute].map(&:to_s)
  errors.size == 1 ? errors.first : errors
end

When called, this could return either an Array of Strings, a String, or nil. It’s up to the caller to figure out which type of object it’s dealing with. This is a bad idea:

  • The caller has to muddy up its own code with obtrusive type-checking.

  • The caller has to know a lot about the method it’s calling. At a minimum, it needs to know every type of object the method could return and when each type could be returned.

  • You have more edge cases to test. If you want to be confident that your code is doing what it’s supposed to do, you have to try out all three scenarios.

Your methods should be consistent about what they return. If you usually return an Array, do what you need to do to always return an Array. If you usually return a User, but sometimes return nil, you could create a Null User object and return that instead of nil.

You could even be less strict: “I’m going to return something that includes the Taggable module”. You could go more generic: “I’m going to return something with id and name attributes.” The important thing is consistency and making sure your caller knows what to expect.

jQuery is an interesting example. Most jQuery methods return the same kind of Array-like object. Because of this, jQuery’s methods are incredibly composable, and you can do crazy amounts of work in a single line of code.

And in case you were wondering, Rails fixed that method in later versions:

1
2
3
4
# File activemodel/lib/active_model/errors.rb, line 133
def [](attribute)
  get(attribute.to_sym) || set(attribute.to_sym, [])
end

Now it always returns an Array. Simpler for them, and simpler for us.

Kill inconsistency

The next time you find yourself returning “An Array or nil”, just return an Array. Take a look through your codebase and see where you’re using kind_of? and respond_to?. See if you can refactor the methods called by that code to return a single type.

And watch as the assumptions you can make about your return values ripple through your project and simplify all of your nearby code.

After you build a few Rails apps, you’ll begin to have some preferred ways of working with them. Maybe you always want to use awesome_print in your Rails consoles. Or you might want rails new to create projects that use rspec instead of minitest.

Sure, it’s only a little annoying to have to specify these preferences each time you run a command. But they’re easy to forget. And it’s infuriating when a computer doesn’t do what you thought you told it to do:

1
2
3
4
$ rails new best_app_ever
^C (sigh...)
$ rm -r best_app_ever
$ rails new best_app_ever --template=my_template

There is an easier way. With a few small tweaks, you can have these commands remember your preferences, so you won’t have to.

Capture your preferences with .rc files

Lots of Ruby tools give you a simple way to store your preferences using .rc files.

.rc files live in your home directory:

1
2
3
$ ls ~/.*rc
.bashrc       .irbrc      .screenrc
.gemrc        .railsrc    .zshrc

and they give commands like gem, irb, and rails a place to find your preferences. Here are some .rc files you should try using:

.irbrc

When you run rails console, you might want to see all of your objects awesome_printed by default. You could type AwesomePrint.irb! at the beginning of each console session. Or, you can add the command to your ~/.irbrc file:

~/.irbrc
1
2
require "awesome_print"
AwesomePrint.irb!

And the next time you start a rails console, your objects will be printed more awesomely, automatically.

.irbrc is also a great place to put hacky convenience methods that help you play with your objects:

~/.irbrc
1
2
3
4
5
6
7
8
require "awesome_print"
AwesomePrint.irb!

# returns the instance methods on klass 
# that aren't already on Object
def m(klass)
  klass.public_instance_methods - Object.public_instance_methods
end
1
2
3
4
5
6
7
8
~/Source/rcfiles jweiss$ bin/rails c
Loading development environment (Rails 4.1.1)
irb(main):001:0> m ActiveRecord::Base
[
    [  0]                                        [](attr_name)                     ActiveRecord::Base (ActiveRecord::AttributeMethods) (unbound)
    [  1]                                       []=(attr_name, value)              ActiveRecord::Base (ActiveRecord::AttributeMethods) (unbound)
    [  2]                         _commit_callbacks()
... about 200 more methods...

When you customize your environment like this, you’ll begin to feel like the libraries you use are yours, instead of just libraries that you use.

.gemrc

Rubygems has its own .rc file to make dealing with gems more convenient. Lots of people use .gemrc to skip rdoc and ri generation, which makes gems install much faster:

~/.gemrc
1
gem: --no-document

But there’s a lot more you can do. There are a few other values you can tweak in your .gemrc, including :sources: (to look for gems on other gem servers) and :backtrace: (to see a full backtrace when errors occur):

~/.gemrc
1
2
3
4
5
gem: --no-document
:backtrace: true
:sources:
- https://my.internal.gem.server
- https://rubygems.org

For the rest of the settings you can place in this file, take a look at the gem environment command documentation.

.railsrc

Years ago, Rails 3 made it easy to generate apps that don’t use the default Rails stack. If you want an app that doesn’t use minitest (so you can include rspec later, for example), it’s as simple as:

1
$ rails new my_awesome_app --skip-test-unit

Or maybe the place you work has their own project template:

1
$ rails new my_awesome_corporate_app --template=http://example.com/my_app_template

Once you get familiar with a Rails stack, you’ll probably want to use it for all your new projects. But it’s easy to forget to add these parameters when you generate another app. Instead, you can put these parameters in a .railsrc file:

~/.railsrc
1
2
--template=http://example.com/my_app_template
--database=postgresql

And now you can run rails new with the right parameters every time. And if you ever want to override those parameters for a single project, you can do it with --no-skip-test-unit-style commands.

Make your environment yours

A little up-front configuration work can make your environment feel more like home. You can skip the pain of accidentally running a command without the parameters you want. You don’t always have to dig up documentation to find the right options. And you can get to the fun part of writing code right away.

Angular vs Ember. RSpec vs Minitest. Haml vs Slim vs ERB. You have so many choices to make when you start a new project. There are vocal defenders on each side. And soon you start to realize that you could have started your project in the time you’ve wasted reading that fourth tutorial or 30-comment argument about whether Sass is better than Less.

So how do you choose the right library, so you can start writing actual code?

You’re under some serious pressure

The development community moves really fast. New libraries appear every day, built to solve all kinds of problems. Many of those solve problems that have already been solved before. And many of those are built as reactions to the libraries that came before.

These debates get personal. People have spent part of their lives building experience using these libraries. Some have been burned by the problems their new favorite libraries were built to solve. So the arguments for using a particular library are passionate and emotional.

When you read all these passionate arguments, it’s easy to feel like you’ll be looked down on if you make the wrong decision.

How do you get comfortable picking a library?

It’s clear that not everyone will agree with the choice you make. The best you can do is be personally satisfied with your decision. You’ll have to be happy with the library you choose on a technical level, as well as a personal level.

On the technical side, I wrote a guide to picking the right gem for your projects, and most of that advice holds for any of these kind of choices.

Your personal preferences will also factor into your choice. Without building an app that uses the library, though, it can be hard to predict which library you’ll prefer to use.

GitHub’s code search can help you make those decisions. When you search for some class names from the libraries you’re thinking of using, you can find projects that use each library. (You can usually get those class names from the README or gem documentation).

From the code, you can see how the library is used, how well it integrates with the code and other libraries, and how simple it is to use. This helps a lot, especially when you don’t have enough context to make a decision based on technical arguments.

Does it even matter?

Making the wrong choice can cause you some pain later on. But when it comes to building software, Failure 0 is failing to start the project. Spending too much time picking the right library for your app does nothing to help you prevent that failure. Most of the time, making any choice will help you much more than making the wrong choice hurts you.

You shouldn’t feel bad about the choices you make. There’s nothing wrong with learning new libraries, trying them out on projects, and making personal and technical judgements about which libraries you prefer to use.

Even if you change your mind later, the things you learned from the library you chose will always stay with you. You can learn a new library much faster as a diff against your existing knowledge than studying it from scratch.

A long time ago, I could have just as easily become a Pythonista instead of a Rubyist. I may not have been quite as happy with it. (Although honestly, who knows?) But I’d still be writing code, and I’d still love it.

(I originally wrote this article for my newsletter. If you want to read more stuff like this, you should sign up!)

The Rails community has an intense focus on testing, and, specifically, TDD. This culture of testing is one of the best things about Rails. But the pressure to test, and test right, can be overwhelming for newcomers.

If you’re coming from a place where you weren’t testing at all, TDD can be especially hard to learn. Think about all the things you need to know in order to do TDD right:

  • You have to know which features you want to implement
  • You have to be able to identify which parts of each feature should be tested
  • You have to learn a testing tool
  • You have to know how to write some tests for each of those parts
  • You have to know how to write a test without seeing the code first
  • You have to know how to write the ‘simplest thing that could possibly work’
  • You have to learn how to use your tests to grow the design of the code

When it’s written out like that, I’d be surprised if you didn’t struggle. It’s clear that you can’t really go from zero to TDD in a single step. So, what do you do? How can you learn TDD without being overwhelmed?

Break it apart!

When you feel like you’re facing an impossible task or you don’t know what to do next, see if there’s something easier you could learn that would get you closer to where you want to be.

For example, to learn TDD with RSpec, you could break it apart like this:

  • Write some exploratory code for a feature without tests. (So you can get some experience coming up with and writing features)

  • Study some code and write down some ways that the code might break on different kinds of input. (So you can learn about what to test and how to test)

  • Write tests for code that already exists, using the minitest built into Ruby (So you can learn minitest and basic testing, without having to learn TDD and RSpec, or fight with installing gems and getting your RSpec environment set up)

  • Test-drive some code using TDD and minitest (So you can focus on TDD without having to deal with gem dependencies or learning RSpec. Since you already know how to write tests, you just have to learn to write them first)

  • Test-drive some code using RSpec (once you’ve already practiced a bit with TDD, so you can focus on getting RSpec set up and learn its API and culture)

Each of these is a much smaller skill that you can practice separately. You can then focus on and practice the first step, then the next, and so on. Instead of jumping straight to TDD with RSpec, you can grow toward it.

It seems like a lot more work. But you’ll learn each of these skills faster than trying to learn TDD from scratch, and you’ll have a better foundation to build on when the next helpful testing style comes along.

Focus on one thing at a time

The key is to focus and practice on the thing just beyond your comfort zone, without trying to leap straight to the last step. Each of these steps has some value in itself. Even if you stopped after the first step, you’d still learn something valuable. But they also build on one another so you can focus on learning one thing really well, instead of having a bunch of things all thrown at you at once. You can learn without getting overwhelmed and giving up.

As you go through this process, give yourself permission to mess up. You’re learning, it’s totally OK! It’ll get a lot easier as you do it more often. Practice consistently and thoughtfully, and keep reaching one level beyond what you know already, and you’ll get there.

While you’re building software, there will be things that will frustrate you every time you have to write them. Bits of code that look ugly. Or those lines you can never quite remember how to write, so you find an example somewhere else in your codebase to copy and paste. These break your flow! So it’s important to recognize them, and make them more convenient.

For example, you’ll sometimes need custom configuration in your Rails app. Maybe you want some extra app-specific settings. Or maybe the libraries you’re using don’t want to look in config/ for their own configuration files. Normally, you’d do the YAML-ERB-read-Rails.env dance:

1
  YAML.load(ERB.new(File.read(Rails.root.join('config', 'google_analytics.yml'))).result)[Rails.env]

But that’s kind of ridiculous. And sometimes you’ll forget the ERB, and things’ll break when you’re not expecting it.

In our codebase, we have a simple Settings class to make this more convenient:

1
2
3
4
require 'settings'

GoogleAnalyticsSettings = Settings.new(:google_analytics)
GoogleAnalyticsSettings.google_analytics_id # => "UA-XXXXXXX-1"

Settings will automatically find the correct configuration file in the config/ directory. Then, it pipes it through YAML and ERB, and uses Rails.env to grab the configuration for the Rails environment. Finally, it wraps everything in an OpenStruct to make accessing the top-level configuration a little nicer. Here’s a gist of a basic Settings implementation.

It’s really simple. But it’s convenient. It’s much easier to remember Settings.new than all that file loading stuff. And these little conveniences add up, and will make your codebase so much more fun to work in.

In your code, can you think of something that annoys you, breaks your flow, or where you have to look up an example every time? Can you find a way to make it easier for you to use?