You’re ready to launch your first production app, and it’s time to get it talking to some external services. You still have to get everything hooked up. So what’s the best way to configure your services in production, without making things more complicated on your dev machine?
Set up your Environment
To configure production apps, today’s best practice is to use environment variables (those
It’s harder to accidentally commit your production keys.
If you’re not paying attention, you might
git pusha file with important secret keys in it. And that could be an expensive mistake.
Configuration is what environment variables are there for.
Environment variables are a common way to configure apps on almost every kind of system. Many other programs (like Ruby) use environment variables for configuration, so it only makes sense to try in your own app.
Environment variables are easy to set up in production.
Heroku has a a web UI and a command line tool for easily setting environment variables. And if you’re building your own server, server management tools like Chef and Docker make setting environment variables easy.
What does it look like on the Rails side?
This is how an app that depends on environment variables could configure itself:
1 2 3
1 2 3
The initializer uses Rails 4.2’s
config_for method to find the right
.yml file and pick the right environment.
config_for runs the
ERB code inside
my_service.yml, and grabs
MY_SERVICE_PORT out of the environment. It passes those values along to
You could also just have the initializer read from
ENV["MY_SERVICE_HOST"] directly. But I prefer to keep them in
.yml files, for reasons you’ll see in a minute.
Your app’s configuration in development
Environment variables are fine for production. But once you set up your production config, how do you handle development and test mode?
You have a few options. But I usually follow the convention in Rails’
config/secrets.yml: use environment variables in production, and hardcode non-secret values in development and test.
With the development and test environments,
config/my_service.yml could look like this:
1 2 3 4 5 6 7 8 9 10 11
Awesomely enough, the initializer can stay exactly the same. The values in this file will be used in the development and test environments, and the production environment will get its values from the environment variables.
But why would you hardcode these values?
The configuration values are easier to see and change.
You can tweak your config as you experiment with new features, which is something you want in development, but not so much in production.
It’s easier for someone new to get started.
If all the sample config you need is checked into your git repository, a new dev just has to clone and run your app. They won’t need to muck around with setting just the right values to get the app working.
You don’t have to worry about conflicting environment variables.
You’ll probably work on more apps on your dev machine than you’ll ever deploy to a single production machine. If you used system-wide environment variables to configure all those apps, there’s a good chance two of them will stomp on each other.
So, try using environment variables in production, and hardcoded
.yml config in development. It’s easy, it’s readable, and Rails has built-in support for dealing with exactly those kinds of config files.
Another option for development
There’s another way to handle configuration in development mode: dotenv. It looks neat, but I haven’t tried it in an app of my own yet.
With dotenv, you can put environment variables in a file named
.env in your Rails app’s root directory, and those values will get picked up by your app. This is nice, because your development environment acts more like your production environment. That’s a good way to avoid bugs that only ever happen in production.
It’s something I’ll try someday. But for now, I haven’t found anything more convenient than
Most production apps need some kind of configuration. So when you deploy your next app, try using
.yml files, populated by environment variables in production, to configure it. You’ll get the flexibility, the simplicity, and the reliability you’re hoping for.
Do you have a different way you like to configure your production apps? Leave a comment, I’d love to hear about it!
Is your GitHub contribution chart solid gray? You could use an open source project to work on. But you don’t have to start it from scratch. The easiest way to create a useful side project is to pull it out of the app you’re already building. That’s how Rails was born!
But how do you know what to extract? And how do you turn it into a gem, without destroying your workflow?
Find the code you want to extract.
Somewhere, deep inside your app, is some code that doesn’t belong there. Code that doesn’t need your app to do its job. Where is it?
Sometimes, you’ll just have to guess. But I often find extractable code in the same few places:
Have you written any custom validations for your attributes? Those can make great gems.
Changes you’ve made to Rails
Every once in a while, you’ll have to mess with Rails to get commit hooks working in tests or blank attributes to turn into NULL in the database. Whenever I’ve moved this kind of logic into a gem, it’s become more stable and easier to understand.
Do you do so much email address or phone number parsing that you moved it into its own class? These classes are often useful in other apps, and they’re pretty easy to turn into gems.
Mock objects and custom assertions
You can write more readable tests by using custom assertions. And once you have some good custom assertions written for a library or pattern, they’re helpful to anyone else who uses that library or pattern.
You don’t have to think big. Some of my favorite gems are just one file!
And if you still can’t decide what to extract, browse the category list on RubyToolbox for some inspiration.
Put your code in a gem-like directory
Once you know which code you’ll turn into a gem, move that code around so it fits a gem-like structure inside your app.
In Rails apps, I use
lib/ as a gem staging area.
lib/ is where I put code that has the potential to turn into its own gem. So, if you’re creating a gem called “json_api_client”, your Rails app’s
lib/ directory might look like:
1 2 3 4 5
Most gems will have a file under
lib/ named after the gem (
lib/json_api_client.rb), and a bunch of files in a directory named after that gem (everything under
lib/json_api_client/). So, take that same structure and match it inside your Rails app. That will make it much easier to move code into the gem later on.
If you’re confused about what a gem layout looks like, take a look at some of your favorite gems’ source on GitHub. You’ll pick up the pattern pretty quickly.
What about tests?
I used to follow
lib/’s structure inside
1 2 3 4 5
It worked OK, even if putting models and libraries in the same folder got a little messy.
Now, though, Rails uses
test/models/ instead of
test/unit/. And storing your
lib/ tests inside
test/models/ doesn’t make a whole lot of sense. I haven’t really decided on a convention for this yet. Do you have any suggestions?
Break the dependencies
Once your code is inside a gem, it won’t be able to depend on your app. This means you’ll have to go through the code you put in
lib/, and look for places where it depends on classes, objects, or behavior specific to your app.
Create a gem
bundle gem to create my gems. Specifically, to create a gem called
-t adds some test helper files and the test tasks to the gem’s
You’ll have to do some housekeeping next, like filling out the
.gemspec, writing the
README, picking a
LICENSE, all that stuff.
And then, since you already have your gem’s code in your Rails app’s
lib/ folder, you can just move that code and its tests into the
test/ folders in your new gem.
A lot of the time, there’ll be things you missed or forgot about, or code that assumes things about your app that aren’t true inside a gem. So, before you move on, run the tests you moved into your gem, and make sure they all pass.
Use your new gem in your app
Now that you have a gem, you want to use it, right? But testing changes to your gem inside your Rails app can get annoying, quickly. You have to:
- Make the change in your gem
- Build the gem
- Remove all traces of the gem from your system, or update the version
- Install the gem
- Restart your server
This is pretty awful. Luckily, bundler gives you an easier way. Say you’ve created your gem in
~/Source/bulk_cache_fetcher. While you’re testing gem changes, you can write this inside your Rails app’s
bundle install, and you’ll be able to make changes to your gem as if that code still lived in your app’s
One last thing: make sure you remove the
path: before you check in your code! That path may not point to the right place on other systems, so chances are it won’t work anywhere except your machine.
Build, ship, and enjoy!
Once your gem is ready, you can send it out to the world.
So, sign up for an account on RubyGems if you haven’t already, check in your changes, and run
rake release. Congratulations, you’re now a gem author! And once you push that gem to github, you’ll get your nice green square for the day.
Do you have any parts of your own apps that seem extractable? Anything that you think might make a good gem? I’d love to hear about it – just leave a comment below.
And if you want to learn a ton more about creating, managing, and maintaining Ruby gems, I highly recommend Brandon Hilkert’s Build a Ruby Gem. The process his book follows is very close to this one, and it covers a whole lot more.
Practicing Rails, my book on learning Rails without being overwhelmed, is now available. And you can still get 25% off until 11:59 PM Thursday night! (Pacific time)
Pick it up here: https://www.justinweiss.com/practicing-rails
A little background:
While I’ve written here, I’ve heard from so many people who have gone through introductory Rails books and videos, have learned the different parts of Rails, but still can’t get beyond the tutorial Rails apps.
They heard it was supposed to be fun, but it was actually just frustrating. They didn’t know where to start. The things they learned didn’t stick with them. They couldn’t spend time learning consistently enough to grow as a Rails developer. Most of all, they were overwhelmed, because the space of possibilities in front of them seemed too big.
Tutorials seem so easy when they’re words on a page. But when you try to transform your ideas into code, all you feel is stress, frustration, and fear. You don’t have the time to learn it all over again, so did you spend that time reading for nothing? Did you waste all that money you spent on bootcamps and video courses?
Practicing Rails will give you the processes, the structure, and the help you need to finally become the Rails developer you know you should be.
If you want to build a new Rails app from the ideas you have in your head…
Learn step-by-step ways to test your code easily and efficiently…
Know what to do when your apps blow up, and you have no idea how to fix them…
Know which parts of Rails to learn now , and what you can put off until later…
Get just the important information out of the noisy Rails community…
And keep your motivation fired up, so you can turn mastering Rails into a habit you’ll actually keep…
This book is written for you.
P.S. At 11:59 PM on February 12th, the price will go up. So if you want to transform from a tutorial-follower to an app-builder, now’s the best time to buy.
While you write code, questions pop into your head constantly: How do I call this method? What options does it take? What happens if I pass an object instead of a number?
These questions destroy your productivity. When you don’t know the answer right away, you forget what you were doing. You get yanked out of your flow, and it takes a half hour to get back to where you left off.
So how do you find the answers you need without slowing down?
Speed is key when you look up API documentation. You want to get your answer and get out as quickly as possible. Otherwise, it’s way too easy to get off-track. I can’t count how often I’ve needed to know the parameters to a method and somehow ended up looking at embarrassing text messages on reddit for an hour.
For speedy documentation, you’ll want Dash, for Mac, or Zeal, for everything else. With these tools, you can hit two keys on your keyboard and bring up the docs you need without having to think about what you’re doing.
But they need a tiny bit of setup first.
Once you download either of these apps, do these two things:
Download the docsets for the libraries you use.
concatmethod you actually mean.
Set up the Global Search Shortcut. On the Mac, I usually use option-Space.
This is what transforms Dash from “just another doc site” into a totally essential tool. Once you set up the global search shortcut, you can hit option-Space, start typing, see the docs you need, hit option-Space again, and have your answer without even realizing what you just did. You can answer your API questions before you knew you had them.
Details, questions, and comments
Unfortunately, sometimes the API docs don’t have all the details you want to know. Or you might need to know how a feature works on the inside.
You’ll have to sacrifice speed to answer these questions. But there are two documentation sites that replace that speed with detail and community.
APIDock only covers Ruby, Rails, and RSpec. But it’s still helpful:
APIDock will tell you when Rails methods were deprecated, and what you should be using instead. It keeps track of version-to-version changes in Rails all the way back to 1.0. So if you’re working with legacy Rails code, it’s an incredible resource.
Lots of API methods have comments, contributed by Rails developers. The comments are really good. With APIDock, you’ll hear about edge cases and common problems before you run into them.
Omniref is new, and has some neat ideas. It has API docs, just like Dash and APIDock. But it also has StackOverflow-like Q&A on the methods, classes, or gems you’re looking at. And when that doesn’t solve your problem, you can see the source code right next to the docs.
This is a great way to get familiar with Rails and gem internals. If the documentation doesn’t answer your question, you can go straight to the source. And once you figure it out, you can leave a note right in the code for the next person to come along.
Your new documentation workflow
So, how do you put all this together?
- When you have a question about an API, use a hotkey to quickly bring up Dash or Zeal.
- If you need more detail, go to the web. Try Omniref first, except:
- If you’re searching for an API in an older Rails app, use APIDock.
When you can instantly find documentation, it’ll transform your Rails productivity. You’ll not only stay more focused, you’ll learn even more about the libraries and frameworks you depend on.
You can’t know everything about every tool you use. You’ll always rely on documentation at some level. So spend a little time now to save time when you look things up. It’ll pay off massively through your entire programming career.
You just changed teams at work, or just started a new job. Or you found a bug in your favorite open-source app, and want to write your first pull request. But after you
git clone and open
app/models, you’re totally lost. Even though the Rails structure is the same as what you’re used to, it’s impossible to find your way around the code. So what’s the fastest way to learn this new, unfamiliar Rails app?
Build a project vocabulary
What is a
Player, and what’s a
Session? How about an
Account vs a
User vs a
Every app uses different terms and different metaphors. And if you don’t understand the vocabulary of an app, you’ll waste time browsing
app/models every time you see a new class name. Or worse, you’ll make bad assumptions about how features work.
So, the first thing you have to do is build that vocabulary. Learn what this app is made of. And if you want to do that quickly, start by reading
db/schema.rb. You’ll learn the terms the app uses, and how all its data fits together. Pay special attention to the
*_id columns, so you can see how the models connect to one another.
If you’re more visual, try the rails-erd gem. It generates a diagram from your Rails models, showing all your tables and columns and how they interact. Keep a printout on your desk, and you’ll have an easier time understanding the rest of your app.
Continue with the models and plain Ruby objects
In a well-designed Rails app, not everything will be backed by a database table. To continue to build your project vocabulary, look inside
app/models for extra classes and attributes that you didn’t see inside
Don’t spend too much time here yet. It’s easy to get lost in how things work. You’re just here to fill out your project vocabulary.
As you learn more about the parts your app is built from, you might wonder why certain objects exist, or why they fit together the way they do. And that’s the point of the next stage.
Try the app, and find out “why”
Once you feel comfortable with the objects the app uses, start the app up. Click around and explore. If there’s documentation, it can help guide you. Otherwise, just see those terms you just learned come to life.
As you use the app, you’ll start to see it from a higher level. You’ll notice that there are reasons why models have the associations they do. Why they’re grouped together. Why those class names were chosen. Maybe they all appear on pages together, or are built from the same form.
And when you start to understand the parts the app is built from and why they exist, you’re ready to figure out how it all works.
Figure out the “how”
By now, you know enough about the app to learn it in detail. You won’t get distracted by new terms and new interactions. You can pick one thing you want to learn, and follow it all the way through.
There are lots of ways you can go from here. These are a few of my favorites:
- Browse the UI, and think: How could this feature be built, knowing what you know about the pieces? Can you guess? Use a debugger to confirm your guess, or discover how it actually works.
- Pick a test case and follow it all the way through the app. Either read through the code, or explore it with a debugger.
- Pick a route or controller action. Can you figure out how to reach that controller action from the UI?
This is also a good time to go back through your models and plain Ruby objects. And this time, you can go into the details of how the models work.
Become curious about the app. Find gaps in your knowledge and close them with a debugger and the code.
Why not start with the tests?
I’ve always heard people say, “If you want to understand a codebase, start with the tests! Tests are executable documentation!”
But this has never worked for me. Test cases are too narrow, too detailed. If I learn from the tests, I feel like I’m missing the bigger point of the app.
In order to understand something as big as a Rails app, or even most gems, I’ve always needed an overview first. I have to know which components make up the app, and why they exist.
But absolutely, make sure the tests run. If they don’t, you’re working with a broken system. And you can’t rely on the things you discover from a broken system.
What, to why, to how
To understand a new Rails app, go through these three phases, in this order:
- What parts make up this app?
- Why do those parts exist? Why are they put together that way?
- How does each piece work?
That will give you both the broad point of view and the narrow knowledge you need in order to make that new app yours.
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.
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:
- Upgrade to the newest point release of whichever Rails version your app is on.
- Upgrade any other gems that keep you from
- Fix the tests, committing as needed.
- Fix the deprecation warnings, committing as needed.
- 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.
A few other resources
In the comments, Miha Rekar pointed to RailsDiff, which shows you the difference between new Rails apps generated under different Rails versions. This is a fantastic way to learn about configuration parameters you don’t need anymore, or new ones you should add to your app.
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?
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
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
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
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
DateTimeinstead of patching it.
Instead, put your monkey patches in a module:
1 2 3 4 5 6 7 8 9
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:
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
would go into
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_son 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
ArgumentErrorwith a good message when you see input you’re not expecting. Don’t depend on someone else understanding random
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Do something like:
1 2 3 4 5 6 7 8 9
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
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:
- Search and Filter Rails Models Without Bloating Your Controller
- The Lesser-known Features in Rails 4.2
- 4 Simple Memoization Patterns in Ruby (and One Gem)
- Rails 5, Module#prepend, and the End of
- How to Beat Procrastination on Your New Rails Project
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:
- A guest spot on the Ruby on Rails podcast, where I talked about some of the behind-the-scenes of the site and the book.
- A presentation to Cali Ruby on getting the most out of Rails tests.
- A guest episode for RubyTapas, on using the
tsortRuby library to handle trees of dependencies.
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.
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 pushand 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.