My Ruby on Rails stack for side projects in 2021

This is the tools and gems that I recommend using in most Ruby on Rails projects in 2021.


Mon Mar 01 2021 - 17 min read
ruby-on-rails

Why would you start a new side project in Ruby on Rails in 2021? Shouldn't you use something like Next.js or even Blitz? Well, I find that Ruby on Rails is still one of the most productive ways to create an application. It is safe and boring and it gets the job done.

With HTML over the wire becoming popular with tools like Phoenix LiveView and Laravel Livewire, you can't go wrong with good old Ruby on Rails. You can even try Hotwire to stream HTML over web sockets.

With all that said I thought I'd share my recommended stack for Ruby on Rails development in 2021.

TLDR

This is long, so if you don't want to read the whole thing then here is a quick summary:

Ruby and Rails versions: Ruby 3 and Ruby on Rails 6
Architecture: Use "the Rails way"
Background jobs: Sidekiq
Database: PostgreSQL
Asset Management: Webpacker
CSS: Tailwind
Javascript: Alpine.js, Stimulus, and Vue
Pagination: Pagy
Testing: RSpec or Minitest with VCR
Periodic jobs: Clockwork
Code formatting: Prettier for Javascript. Rubocop for Ruby
Exception management: Rollbar or Honeybadger
Sending email: Postmark
Slugs: friendly_id
Full Text Search: pg_search or searchkick
Deployment: Heroku or Dokku
Self-hosted tools: Thumbor, Huginn, OpenFaaS and Grafana
Editor tools: Solargraph, Rails fast nav, Prettier, ruby-rubocop
Monitoring: InfluxDB, Grafana and influxdb-rails
Templating engine: ERB
Admin tool: activeadmin

Ruby and Rails versions

Use the latest Ruby and the latest Rails versions. This is currently Ruby 3.0 and Rails 6.1. They both work well together and I haven't found any reason to stay on the 2.x Ruby versions.

Rails architecture

Caveat: This is just my opinion and if you do things differently then that is great too!

If you are building a side project with Ruby on Rails then I think that you should stick to the Rails way of doing things. Do not reach for fancy things like DDD or hexagonal architecture, especially not if your app is small. There is a very real YAGNI factor when it comes to software architecture and you can waste a lot of time designing something that you don't need.

Don't introduce decorators and view models. Use helpers instead.
Don't extract domain models. Put the code in the ActiveRecord models and the controllers.
Don't reach for interactors to model your domain logic.
Don't try to avoid duplication too early.

I have done multiple apps that need all of the above, but those were huge business critical projects with tens of thousands of lines of code. I've started out with an advanced design from the start so many times and then realized that I didn't even need it.

Once you see a pattern emerging in your app you can start thinking about these things. Perhaps your views got all cluttered with presentation logic that don't really belong in your model. Then, and only then, should you think about perhaps introducing decorators or view components.

Your codebase will eventually tell you what it wants to be and once you are experienced enough you will be able to tell when you need to restructure things.

It may look pretty but it is not always practical

Background jobs

Sidekiq. I've been using that for many years now and it has been rock solid both for side projects and work projects. I also recommend using ActiveJob with Sidekiq just to make things a bit easier and rails-like. It will make things a bit slower but that shouldn't matter for side projects.

One issue with Sidekiq is that you won't have access to rate limiting and scheduled jobs unless you pay for the enterprise version. If you are running a business then this is probably a good idea but we are going for dirt cheap here. The rate limit part can be solved using something like ruby-limiter for global rate limiting and sidekiq_limit_fetch to limit concurrency per queue. It doesn't have official support for modern Sidekiq versions but in my experience it works fine anyway. You should probably not use that for business critical things however.

If you want to get fancy you can also take a look at Faktory, which is a polyglot job processing framework from the creator of Sidekiq. Then you can write your jobs in any language you want as well.

Database

Use a relational database such as PostgreSQL or MySQL. I do not recommend using document databases such as MongoDB because there is really no reason for it in my opinion. PostgreSQL is plenty fast and you probably won't need things like horizontal scaling for a side project. You can of course try something fancy like FaunaDB, CockroachDB, or any of the many database flavors.

This might come off as old school, but when I develop side projects I need something that stores my data safely and is easy to backup with rock solid performance. PostgreSQL checks all those boxes and it has always served me well. Let me put it his way: There is no reason to ponder "is my data safe" when using PostgreSQL.

Even the logo is amazing.

Asset Management

Two ways: Asset Pipeline or Webpacker. I always use Webpacker. It is modern, you have access to the full NPM ecosystem, and it has all the modern features such as tree shaking and hot module replacement.

Webpacker itself is just a Rails style wrapper on top of Webpack. You probably don't have to worry about configuring webpack if you use Webpacker so that is great.

CSS

OK I realize that this is highly opinionated so I'm just going to give you my recommendation: I'm a big fan of Tailwind. I find that its utility-based approach is great for getting things done. I like it so much that I even paid for the premium component package TailwindUI. It is just one of those things that you either like or don't like. Since you already use Webpacker(right?) you can use whatever you want so find something you like, learn everything about it, and then stick to it.

Javascript

I have divided this into three sections to make it easier to recommend things.
Javascript is one of those things where you can really screw yourself over by choosing the wrong thing. I recommend starting very small and only expand when you know that you need it.

Just a little Javascript
This is when you need things like "show this element when I click this button" and "show the value of this input field in this h2-tag as I type it". For this I recommend using Alpine.js. You don't need to juggle script tags and jQuery with Alpine and it gets the job done when you need something fast and simple. It can however gunk up your HTML quite a bit so when you get to that point you might want to move on to the next section.

Some Javascript
Once you need to introduce NPM packages and need something more organized then Alpine might get messy. At this point I would introduce Stimulus. It is made by the creators of Ruby of Rails. It will clean up a lot of gunk introduced by Alpine and leave you with reusable controllers. Here you can import NPM packages and it works very well with Webpacker. You can of course keep using Alpine as well and use Stimulus for the more advanced things if you wish since they work very well together.

A lot of Javascript
You might not reach this stage and you should be very careful about choosing to go down this path prematurely since it is quite a bit of work. If you need advanced state management, high performance and complicated client side logic then Stimulus might no longer cut it. You may have to reach for a declarative, component based library like React, Vue, or Svelte. All of these, and others, work very well with Webpacker and there are wrappers to make things easier for you. For example we have React on Rails for React integration.

Again, you need to be careful about taking this step because once you start using something like React you have to replace your HTML with React components to use it. You cannot simply have React "use your HTML" as a template. You have to rewrite everything from that DOM node and "down" to be able to use React properly. However, if you use Vue then you can reuse your existing HTML as the template in Vue. This might make the transition easier and much like Stimulus did Vue will become "Javascript sprinkles" on top of your existing HTML. I don't think that something similar can be done with React. Hit me up on Twitter if you know of a way!


But Magnus, what about Hotwire or StimulusReflex? If you send HTML over the wire then you won't need to add any advanced Javascript at all right? The simple answer is that I don't have enough experience with these tools to be able to recommend them. They seem very promising however and I will absolutely write an article about this technology once I have worked with them some more.

Bottom line: Do not reach for something advanced unless you know that you need it. Stick with simple things like Alpine.js until you are absolutely sure that you need something else.

Caching

Since you already use Redis to get Sidekiq going then you might as well use that for your cache as well.

For the actual caching part you should use Russian Doll Caching, also known as Fragment Caching. This is the preferred way of caching things in Rails. I also would like you recommend using Action Caching if you can get away with it. It was removed back in Ruby on Rails 4 but there is a gem that you can use. It was removed from Rails because it can be a hassle to get the cache expiration to work properly and such. I try to use it whenever I can however because it is so much faster.

I wrote more about caching in my article about Ruby on Rails performance so you might want to check that out as well.

Pagination

I've been using Kaminari in the past but I've since switched to Pagy. It has all the bells and whistles that you can think of like countless pagination, pagination using AJAX, and built-in helpers for many CSS frameworks.

Testing

This should probably be a full article. I could write about what to test, when to test it and how much to test. I will probably make this into an article of its own so I'll just stick to writing about which frameworks and tools I recommend here.

This will be a bit of a boring one since you really can't go wrong with either RSpec or Minitest and it is a matter of preference. If you want something that is close to bare-bones Ruby then use Minitest. If you want something declarative and like having DSLs for things then use RSpec. I find that RSpec is a bit easier to organize and its helpers are nice. Personally I use them both but I've been leaning towards Minitest lately since that is the Rails default. It also has parallel tests out of the box while you have to use something like parallel_tests to get that going in RSpec. If you test suite is large then having built-in parallelization is fantastic.

For test setup I use Factorybot instead of fixtures since I find that to be much easier to reason about.

Finally, a word about mocking external APIs. You can do this in many ways from setting up manual mocks using Webmock, to using fake servers with Sinatra or WebValve, all the way to replaying previous calls using VCR. They all have their pros and cons, but for fast development I'm a big fan of VCR. You don't have to muck around with setting up mocks with fake data loaded from fixtures. VCR however can be messy too since you literally replaying past requests in your tests. The fixtures created by VCR can also become large and slow. For very fast development however, it is what I recommend. Once you run into enough problems I would take a look at the fake server approach instead.

Periodic jobs

There are many tools for this. For Sidekiq you can use its built-in periodic jobs feature. That is only for the enterprise version however. There are a number of gems with similar functionality like sidekiq-cron and sidekiq-scheduler. There is also the Clockwork gem that is not tied to Sidekiq. Personally I use Clockwork, since that is not tied to Sidekiq and it has been working well for many years. This will however start another Rails process to do the scheduling so there will be extra RAM use.

Code formatting

You should absolutely use code formatting tools that run every single time you save your files. They take a lot of work out of coding so that you can focus on implementing features.

For Javascript code I use Prettier. It comes with reasonable defaults and a non-approach to settings. It is opinionated, gets the job done and there isn't a million things to configure.

For Ruby I use Rubocop. You can set this up so that it runs using Spring by generating a binstub using spring-commands-rubocop. It will make it run a bit faster. Rubocop also has plugins for your testing framework.

For ERB I use ERB Formatter/Beautify. It works very well if you use Visual Studio Code.

Performing HTTP Requests

I used to use Faraday for this but I've switched to the HTTP gem. I find it to be a bit cleaner and easier to use.

Exception management

I use Rollbar to capture errors in production. It has a very generous free tier and it starts at $1 a month once you go past that. Honeybadger is also a great option.

In development mode you should make sure to use better errors to get nicer error pages when things go wrong.

Continuous integration

If you run your tests and deployment on an external service then you have a lot of options. Personally I use Circle CI for all my projects, Ruby or not. It has a very generous free tier as well as extra stuff for open source projects. The paid tier starts at $30 a month which is reasonable.

Sending email

This is a bit of an odd one to be included perhaps but since getting the emails to arrive safely is pretty then this is quite important. I've used Sendgrid and Mailgun in the past but I have found them both to be unreliable. Messages don't arrive correctly sometimes or they get stuck in Microsoft spam filters. I would recommend everyone to use Postmark for all your email needs. They get the job done and you never have to worry about deliverability. I'm not even getting paid to say that; I just like them that much. They are not free however and start at $10 a month for 10000 emails.

Slugs

Sometimes you want a nice URL and perhaps not "myapp.com/movies/1234". Instead you want "myapp.com/movies/parasite". For this you should use the friendly_id gem. It handles every single conceivable feature for slugging such as generating slugs, multiple languages as well as slug history if you need that.

I think there are two reasonable options here. You can either use a service such as Elasticsearch or use built-in functionality in PostgreSQL with the pg_search. I find that pg_search is better if you can get away with it because it is simply using PostgreSQL. You will need to think carefully about indexes if you have a lot of data though since full text search can be very slow.

If you decide to use Elasticsearch then you should use the searchkick gem to make it a breeze.

Finally, you can also use an externally hosted service such as Algolia. Don't immediately reach for that however since you will need to manage indexing yourself and it can become expensive if you have a lot of queries.

Deployment

If you are ok with paying a bit for hosting then just use Heroku and don't look back. It is a "push code to deploy" with hosted databases and everything. They have all you need to deploy and run your app, but it can get quite expensive. I went into this in greater detail in this article I wrote.

If you want the cheapest way possible but still easy then you should use Dokku. It is a self-hosted "mini Heroku". All you need is a cheap server, an hour to install it and then you are off to the races. I went into Dokku in greater detail in the Heroku post mentioned earlier.

If you need a cheap and solid server then feel free to use my referral code to get €20 in credit at Hetzner. Read my review for more information.

Self-hosted tools

These are not Rails-specific but they are very nice to have for any app.

Thumbor
Self-hosted image transformation and optimization tool.

Huginn
Self-hosted Zapier/IFTTT alternative.

OpenFAAS
Self-hosted serverless functions. See this article for more details.

InfluxDB + Telegraf + Grafana
Self-hosted monitoring and visualization stack.

A Grafana server monitoring dashboard

I wrote more about self-hosted tools in this article

Editor tools

There are more examples in my article about Visual Studio Code plugins for Ruby. These are the essentials:

Ruby
Well...you'll need this.

Solargraph
"Go to definition"  in your editor. Code navigation

Rails fast nav
Navigate your Rails apps faster

Prettier
Run and display prettier from within Visual Studio Code

rubo-rubocop
Run your Rubocop cops from within VSCode.

Monitoring

If you're ok with paying some money then use Datadog. It has server monitoring, log management, and application monitoring. If you want something self-hosted then use the InfluxDB+Grafana stack mentioned earlier and then use influxdb-rails to push metrics. It works really well and Grafana can give you some really solid visualizations.

Templating engine

I used to be a huge fan of HAML, but now we have things like Emmet and autoformatting. I also write HTML in React and Vue so the jump to HAML is not great. I highly recommend that you just stick to ERB which is the Rails default. If you have an app that is full of HAML templates then you can use https://haml2erb.org/ to convert them to ERB.

Admin tool

I've tried most of the the admin tools out there and I find that activeadmin is the best choice. It has a very powerful, yet daunting, DSL for generating admin tools. You can have a very decent admin section with little effort using activeadmin, so I highly recommend trying that before writing your own.

Misc gems

In no particular order:

Down: Safe file downloads
Discard: Soft-delete.
Feedjira. RSS management
sitemap_generator. Generate sitemaps with ease
meta-tags. Create SEO tags, Twitter cards and opengraph sharing attributes.
html-pipeline. Tools for doing auto-linking of URLs inside text blocks and more.

Conclusion

Wow that was a long one. Thank you so much for making all the way to the end and I hope that your future Ruby on Rails projects will be successful. If you have any suggestions or have any questions then you can find me on Twitter or just send me an email

Get awesome stuff in your Inbox

I care about the protection of your data. Read my  Privacy Policy.