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.
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.
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:
- 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.
- Find the project on GitHub.
- Read the project’s changelog, readme, or wiki to take me from the version I know to the most recent version.
- If I’m interested in a specific change, google around for it or go through the project’s issues to learn more.
- Upgrade to the newest version of the gem.
- Install the new gem’s documentation in Dash.
- 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
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.
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.
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.
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
1 2 3 4 5 6 7
When called, this could return either an
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
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
Now it always returns an
Array. Simpler for them, and simpler for us.
The next time you find yourself returning “An
nil”, just return an
Array. Take a look through your codebase and see where you’re using
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
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
Lots of Ruby tools give you a simple way to store your preferences using
.rc files live in your home directory:
1 2 3
and they give commands like
rails a place to find your preferences. Here are some
.rc files you should try using:
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
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:
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
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.
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:
But there’s a lot more you can do. There are a few other values you can tweak in your
:sources: (to look for gems on other gem servers) and
:backtrace: (to see a full backtrace when errors occur):
1 2 3 4 5
For the rest of the settings you can place in this file, take a look at the gem environment command documentation.
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:
Or maybe the place you work has their own project 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
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
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.
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
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
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?
When you read Rails blogs and books, or watch conference talks, you’ll learn a lot about making your models skinnier.
These techniques are awesome, because your models will get too big or complex to handle. But do you really want to go as far as having your models only responsible for persistence, associations, and validations? How do you decide how much logic should stay in your ActiveRecord models, anyway?
Skinny. But not too skinny.
Active Record is a pattern that works best when your models closely match your database schema. That’s what it’s designed for! But what does that mean?
If some code does work from the point of view of an ActiveRecord model, it can go into the model.
If some code does work that spans multiple tables/objects, and doesn’t really have a clear owner, it could go into a Service Object.
Anything that’s attribute-like (like attributes calculated from associations or other attributes) should go into your ActiveRecord model.
If you have logic that has to orchestrate the saving or updating of multiple models at once, it should go into an ActiveModel Form Object.
If code is mostly meant for displaying or formatting models in a view, it should go into a Rails helper or a Presenter.
Beyond those guidelines, you should use the same rules you’d use to refactor any class that’s getting too big. But generally, you shouldn’t feel bad about leaving some logic in your ActiveRecord models. They’re meant to have it!
So, since Rails chose the Active Record pattern, it makes sense to have some logic in your models. But why did Rails pick that pattern instead of something cleaner?
What Would Ruby Do?
In Patterns of Enterprise Application Architecture, the Active Record pattern holds the middle ground between Row Data Gateway and Data Mapper. Row Data Gateway is a mostly dumb object-oriented wrapper around table rows, like the skinniest of all possible models. Data Mappers are more complex than Active Records, and are mostly used to convert between objects containing nothing but business logic and objects containing nothing but persistence logic.
So, in the context of Ruby, Active Record is absolutely the correct default pattern.
Why do I say that?
Ruby is designed to make programmers happy. When it’s forced to make tradeoffs between cleanliness and convenience, it almost always chooses convenience.
Array has over a hundred methods in its public API. It has a ton of methods that are just aliases of one another. Because some developers just prefer writing
In that context, it makes a lot of sense that Rails would default to a pattern that’s convenient over one that’s more flexible, or has more object-oriented purity. It’s the Ruby way. And I love it.
You can always refactor to something more flexible when you need to. But then again, YAGNI.