A couple of days ago I was asked by a friend for some help with speeding up a Rails app in general, and in particular with reducing the total time the RSpec suite was taking to run, which was over twenty minutes on my computer. Twenty minutes is not huge in the Rails world, but this app is growing quite quickly and both tests and the app in general are getting slower and slower as they add features etc.
For the testing, they were evaluating a switch to Minitest and fixtures from RSpec and factory_girl (since that combination tends to be faster) but this would take ages and anyway they (like me) prefer RSpec and factory_girl.
After two days looking into it I was pleased with the results: the app now boots in just ~3 seconds without Spring, assets compilation is ridiculously fast - which also helps during deployments - and the whole RSpec suite runs in just around 1’40” on my computer. Not bad!
There were many reasons why the app and the tests were that slow. The main reasons though were:
- lots of gems. Really, lots
- lots of assets, most of them through gems
- several missing databases indexes slowing things down in production
- lots of data created in the database during tests, whereas in many placed build_stubbed would work just as fine
- lazy loading of associations here and there
Most of the optimisations I’ve made are specific to this app, so I won’t get into the details about these. However I wanted to share a few small tips that have helped massively and that can be applied to any Rails/RSpec app.
First thing I did, was to find out whether all of the gems specified in the Gemfile were actually required. I found a few which basically were no longer in use, so I removed these.
By removing these gems, the app took around 8 seconds to boot without Spring, down from ~10. I then found the gem bootsnap by Shopify which does indeed speed up the boot process as it claims; the boot time went down to ~3 seconds by just adding this gem! The README does say that this gem is still “beta quality” but I haven’t seen any issues so far so fingers crossed.
Specifically for the tests, the biggest improvements were:
switch to capybara-webkit by Thoughtbot. Before the app was using Poltergeist, and switching to capybara-webkit yielded a massive gain! I am not sure about the future of capybara-webkit, but for now it just is much, much faster than Poltergeist or Firefox/Chrome (even headless) with Selenium.
These changes reduced dramatically the time taken for the feature specs to run down to ~1’40”.
All in all I am very pleased with the results, especially because it didn’t take too much time. In this quick post I haven’t gotten into the details of how to implement the suggested changes because it’s easy to follow the READMEs on Github, but please let me know in the comments if these work for you.