Should you add Gemfile.lock to Git?

TLDR: Yes for apps. No Yes for Gems.

Do you use version control to keep track of changes to your code base? Or do you think version control is for the cowardly and just change files in a shared team folder and hope no one else is changing it at the same time?

Of course not. You are using version control like a sane, logical programming creature. So why is there such controversy about using version control for your Gemfile.lock? Lets dig in.

First of all this isn’t very controversial in regards to the lock file in applications. Pretty much everyone agrees you should check that into git. Including your Gemfile.lock in version control is standard practice if you are writing an application. The top answer I found on Stack Overflow says: “Assuming you’re not writing a RubyGem, Gemfile.lock should be in your repository.”

The one that can be pretty controversial is if you should check your Gemfile.lock in when building a Ruby gem. The community view seems to have evolved on this over time. There are lots of differing opinions on this so I will try to give you an overview of the arguments about when to commit Gemfile.lock and when to add it to .gitignore.

The argument for including the Gemfile.lock

Including Gemfile.lock means that everyone is working with the exact same dependencies.

This prevents bugs from cropping up on a new teammate’s laptop because they bundle installed a slightly different set of gems than you did. They won’t pull a slightly newer gem that might be broken and have their setup break. They could be sitting there blaming themselves when it was really a gem’s fault.

This also prevents this from happening in production on a new deploy or to a new production box. Imagine if you spin up a new prod box because you are getting more traffic and it installs a newer version of some Gem. It would be difficult to track down why you were getting errors.

Bundle updates can be handled on one machine and pushed out to all the other machines.

You can (and should) specify versions of gems in your Gemfile but without the Gemfile.lock being included in Git. Then nobody else will get to benefit from your work of checking that the updated Gems still work. You should check your Gemfile for updates frequently and bundle update where appropriate. Lets face it though, in every app I have ever worked on any major update to the Gemfile requires a developer to run through the functionality manually to make sure the app is working. This should include running the test suite after a bundle update but it inevitably requires running manual tests for critical functionality (usually on development, then staging, then a final check in production). Gotta be careful and make sure the app is really, really working.

The argument against including the lock file

There is the famous Yehuda Katz article which urges you to not commit your Gemfile.lock to source control when building a gem.

Gems depend on a name and version range, and intentionally don’t care about where exactly the dependencies come from.

Yehuda Katz

Control versions in the Gemfile

The Gemfile should have versions set and this should prevent problems right? So two answers to the two parts of the question:

YES – setting versions for some Gems (like Rails) will keep you on the version you want.

NO – setting versions in the Gemfile won’t prevent problems.

Minor changes in Gem versions can still introduce bugs so just limiting the Gems to minor versions won’t totally prevent errors and even patch levels can introduce bugs.

Will your testing will save you?

A counter argument can be made that a developer should be running their app/gem through testing on a new install to make sure the new development environment is set up and working correctly. Same can be said for using a CI before a deploy which should be testing with a new bundle install on every run and thus get the same dependencies as a new developer would and find any bugs. This would theoretically sort out any problems but requires the the tests to be complete (raise your hand if you completely trust your tests).

The evolution of community ideals

It looks like Rails is tracking the Gemfile.lock now (and it is a gem , of course) and you can see their Gemfile.lock in master.

Tracking the Gemfile.lock also makes bisecting easier. Without the Gemfile.lock being included in git a new install of the gem might return a dependency that introduces a bug. However this new failing code will not be associated with a commit and thus will be harder to track down than a failing test that had been introduced and committed.

It looks like Bundler has changed their tune as well. At first they gitignored the Gemfile.lock but:

Over time, however, it became clear that this practice forces the pain of broken dependencies onto new contributors, while leaving existing contributors potentially unaware of the problem.

https://github.com/bundler/bundler/issues/5879

It is now recommended for all Gems created with Bundler to check in their Gemfile.lock and they have also taken Gemfile.lock out of the generated gitignore in new gems.

Final Thoughts

You should always include your Gemfile.lock if you are writing an application. The community seems to (largely) agree that you should include it in any Gems you create as well. Which will make your bisecting any problems that come from your dependents easier and ensure that the pain of updating dependencies isn’t offloaded to the consumers of the Gem.

Processing…
Success! You're on the list.

Rails Benchmarking

So you want to know fast your Rails app is going?

If you just want to see how fast a bit of code is you can use the Benchmark module which is part of the Ruby Standard Library.

require 'benchmark'

puts Benchmark.measure { (0..9999999).each{|n| n * 99999999} }
#     user       system      total      real
# => 0.770000   0.010000   0.780000 (  0.773784)

If you want to compare two or more bits of Ruby code that do the same thing and see how fast they are against each other then you can use the ‘bm’ method on Benchmark.

require 'benchmark'

Benchmark.bm do |z|
  z.report { x = 0; (0..9999999).each{|n| x += n * 99999999} }
  z.report { x = (0..9999999).map{|n| n * 99999999}.sum }
  z.report { x = 0; for n in 0..9999999; x += n * 99999999; end }
end

#       user     system      total        real
#   1.441087   0.003661   1.444748 (  1.451196)
#   0.837622   0.039052   0.876674 (  0.880564)
#   1.555514   0.004319   1.559833 (  1.566806)

So for this code the second implementation is the fastest by a good margin, which might not have been my first guess.

You can also use the Benchmark method #bmbm to run the code in both rehearsal and then the real tests in case the benchmark results might be uneven because of garbage collection. Running it for the example above returns:

Benchmark.bmbm do |z|
  z.report { x = 0; (0..9999999).each{|n| x += n * 99999999} }
  z.report { x = (0..9999999).map{|n| n * 99999999}.sum }
  z.report { x = 0; for n in 0..9999999; x += n * 99999999; end }
end

Rehearsal ------------------------------------
   0.896595   0.000000   0.896595 (  0.896600)
   0.568865   0.007963   0.576828 (  0.576842)
   0.915542   0.000003   0.915545 (  0.915562)
--------------------------- total: 2.388968sec

       user     system      total        real
   0.877021   0.000008   0.877029 (  0.877058)
   0.566475   0.015973   0.582448 (  0.582493)
   0.905056   0.000000   0.905056 (  0.905080)

You can also measure methods and label the reports like so:

require 'benchmark'

def fast(range, multiple)
  return range.map{|n| n * multiple}.sum
end

def slow(range, multiple)
  x = 0
  for n in range
    x += n * multiple
  end
  return x
end

range = (0..9999999)
multiple = 99999999
Benchmark.bm do |z|
  z.report('Fast method - ') {fast(range, multiple)}
  z.report('Slow method - ') {slow(range, multiple)}
end

# =>                user     system      total        real
# Fast method -   0.573835   0.003838   0.577673 (  0.577681)
# Slow method -   0.908593   0.000827   0.909420 (  0.909443)

Processing…
Success! You're on the list.

Cleaning up a messy Gemfile

Aging Rails apps can become a bit of a mess. Fortunately we can fix this bit by bit. Today we will look at some rules for writing a clean, readable Gemfile.

1. The gems should be split into groups. (Test, Development, etc.)

Any Gemfiles that look like this can get messy as they get longer:

gem 'nokogiri'
gem 'sinatra'

gem 'wirble', group: :development
gem 'faker', group: :test
gem 'rspec', group: :test

gem 'capybara', group: :development
gem 'rspec-rails', group: :development

This can be as many groups as you like but they should be in this format:

# Gems used everywhere.
gem 'nokogiri'
gem 'sinatra'

group :development do
  gem 'wirble'
end

group :test do
  gem 'faker'
  gem 'rspec'
end

group :test, :development do
  gem 'capybara'
  gem 'rspec-rails'
end

2. Within those groups the gems should be alphabetically ordered.

This just makes sense so that they can be found easily. You wouldn’t want to walk into an unordered library, would you?

3. Remove any gems that have bee commented out.

You don’t need to keep the entire history of the Gemfile. That is what git is for.

4. No comments on it’s own line if the comment refers to a gem.

Comments in the Gemfile usually aren’t needed. You can learn what each of the Gems do. So instead of this,

# Thin. Web Server greatness
# Start with: rails s
gem 'thin'

let the comment sit on the line with the gem:

gem 'thin' # Start with rails s

5. Only use apostrophes or quotations marks, not both. Pick one and change the other.

You can pick between gem ‘rails’ or gem “rails” but the style should be consistent.

This isn’t a conclusive list but it should help cut down the size and complexity of your Gemfile.

Processing…
Success! You're on the list.

Ruby off Rails

So, Ruby is well know for its hugely popular gem Rails which greatly simplifies building websites. But what are some other programs built in Ruby? Here are a few very popular examples.

RubyMotion – Develop native apps for iOS (both iPhone and iPad) and OS X in Ruby. It is based on MacRuby which was developed by Apple. The app compiles into native objective-c just like iPhone apps made traditionally.

Artoo – Gets Ruby up and running on Arduino, Leap Motion, Pebble, Rasberry Pi, Ardrone, Roomba and more. Built to let you program devices and the internet of things in Ruby. It also lets you connect multiple devices together through Artoo into an integrated robotic solution.

Rake – A task management tool that allows you to namespace tasks and run them with Ruby it is Ruby’s alternative to make. A simple gem that you can install with ‘gem install rake’. Built by Jim Weirich (who you need to know about), it enables you to run a command from the bash prompt and execute Ruby code inside your app or on the system at large. Wikipedia claims that “It is the most widely downloaded Ruby Gem, downloaded more than 40 million times[12] and has been included with Apple OS X since at least version 10.7.”

Chef – A system provisioning tool that speeds up the initial configuration of servers as well as their maintenance. It hooks into cloud services like AWS and Rackspace. It works on the basis of “recipes” that define how a given task is to be done for example how packages are to be installed and what programs should be running on a server.

Capistrano – It started as mainly a rails deployment tool, but can be used to deploy any language webapps now. It sells itself as a remote server automation tool that can deploy to an arbitrary number of web servers and to automate common development tasks.

Homebrew – A command line package manager for OSX that is a must have for any Ruby developer. Easily install databases like mysql and postgres or download python with ‘brew install python’ to try it out. It is powered by git and ruby so you can hack at it as you like. You can also create your own homebrew packages with ‘brew create’ and share them with the world.

Sinatra – “Put this in your pipe and smoke it.” Sinatra is a lightweight alternative to Rails. Use rvm to ‘gem install sinatra’ and then build out a small webapp.

Gosu – A 2d game creation gem that works on Mac, Windows and Linux.

Ocra – Creates Windows applications from Ruby code. Includes the Ruby code and interpreter in the application.

 

Processing…
Success! You're on the list.

Naming Ruby Gems

I created a gem called tiedye and I wanted to explain a bit of my reasoning behind why I named it that. So, lets talk about naming. Why call this gem tiedye? How do I decide to name Ruby gems in the first place?

First, there are a few community conventions on how to name your gem. I went with the convention of using underscores in the name of the gem (snake_case instead of camelCase). This applies to all gems that have more than two words as their name. So tiedye or rails are one word but will_paginate and delayed_job are two words written in snake_case. Also I suggest you try to find a fun name for your gem. Ruby has a xml parser named nokogiri, which is Japanese for chainsaw and I named my gem that works with colors tiedye because of the mix of colors those awesome hippies love to wear. There are a whole list of good suggestions that you can find here: http://guides.rubygems.org/name-your-gem/

Other rules (which you will find in the previous link) are:

  • Use dashes for extensions
  • Mix underscores and dashes appropriately
  • Don’t use UPPERCASE letters

If you don’t understand those then you should have read the article. Seriously. I don’t want to explain it. Go read the article.

The second thing I did is I went to rubygems.org and search for my desired gem name. With so many gems built to date there is a good chance that someone else already has used that name. You want your gem to have a unique name so that people can remember it and so that you can deploy it to Rubygems.org. Don’t worry, though there are plenty of names still available. Of course, all this only applies if you are trying to make a public gem. If you are making a private gem, then you will be requiring it from a private repo on github and you can name it what you like. Though you might still conflict with other named gems if you decide to name it something like “rails”.

Also for a good read check out: http://unethicalblogger.com/2011/11/13/ten-poorly-chosen-gem-names.html

Processing…
Success! You're on the list.