Ruby on Rails saw a lot of change through 2023, and there were prevailing winds that seemed to nudge people in to making different Rails architecture choices (“stack”), or at least talking about it.

Some of this was inevitably led by the narratives coming out of 37Signals/DHH. Whether we’re all drinking the kool-aid or have found consensus on stacks, only time will tell.

Notable changes in community groupthink oribted around:

  • SQLite being “good enough” for Rails web apps production (I do recognise some irony here)
  • Consolidation of technologies for background workers/queueing technology
  • Turbo/Stimulus/Hotwire changes and upcoming Turbo 8
  • Realising you didn’t have to use Devise

All of this relates heavily to a simplification of stack and reducing the number of moving parts. Not a bad thing.

Rails Stack for 2024

I’ve found most joy writing web apps in Roda but in recent years have focussed more on Rails. I can argue both sides of this coin, but I’ve really enjoyed the direction Rails has been going in the past 2 years.

So, what would a rails new look like for me in 2024? Starting at with the MVC paradigm…

Model Layer

  • Postgres for the database. Used it for years and value the modern sql implementations. If an app was tightly scoped I would consider SQLite more now than I would have done; partly influenced by the community but also SQLite is not as much a “moving part” as Postgres; I would highly value not running a Postgres server (Docker). The “set and forget” nature of SQLite sounds appealing, but I expect to see some correction in groupthink attitude in the next year (“what do you mean I can only have 1 app server?” and “I have to put SQLite on network storage?”)

  • With multi-database apps I would still want to use Sequel to bring in a second database (usually analytical data) but I’m forcing myself to stick with mutli-database Rails setups now to keep the developer experience consistent. In short, use ActiveRecord for everything even if part of me wants to carry on with Sequel

  • Historically I’ve made liberal use of Storext but it’s becoming worryingly unsupported. There are other alternatives to represent Postgres JSONB structures as “first class” model fields I wille xplore

  • prefixed_ids for prefixed model identifiers

  • Strong Migrations to catch any whoopsies before they happen

Controller Layer

  • ServiceActor to build/manage/control business logic. The Rails community can discuss service objects all day (year?) long. This is what I find best. I also admire and respect this summary of the whole area

  • I use some of the dry-rb libraries. Primarly dry-configurable and dry-initializer (the latter with view_component) - I find having a some syntax sugar in classes very helpful

  • authentication-zero for authentication. I am used to having granular control on login (from years of Sinatra/Roda) so prefer this to Devise. I am also using passwordless to do away with passwords, but worry this is not a good long term position for a new app

  • Action Policy for authorisation. Beautifully documented and clearly battle tested by the Evil Martians team. Previously used Pundit.

View Layer

  • high_voltage to easily create/maintain static content pages

  • ViewComponent for reusable components. I create a parent ApplicationComponent with Dry::Initializer to speed up passing of args/params. Phlex also looks interesting but doesn’t seem compelling enough to change to

  • tailwindcss along with everyone else. Used via tailwindcss-rails - I do sometimes miss the simpler world of Tachyons but the Rails tooling around Tailwind is pretty great now

  • Daisy UI for pre-made components/theming. Offers a shortcut to being productive vs pure CSS/tailwind

Reactivity + Javascript Framework

I am honestly at a crossroads here (or stop sign!). Simplification/reduction in choices is a good thing, but I seriously have my (hot)wires crossed with Rails reactivity at the moment.

I want to keep it as pure Rails as possible which would mean investing in Turbo. I have done some Stimulus more recently, but really struggle with the paradigms; I have scenarios where I want to return pure JSON to the frontend but am finding myself running this through an additional Rails view template which doesn’t make sense to me.

Alpine.js still has my heart, and provided you turn off preloading/instantclick etc it should work just fine with Rails. I will experiment in Turbo/Stimulus still, but likely revert to Alpine.js; it’s just so productive.

Moving outside of the MVC paradigm:

Background Job Processing/Job Queue

Historically I followed the herd here and used Sidekiq.

However, I still don’t fully understand what it is doing. Clearly this is my own cognitive limitiations as it is an incredibly impressive and successful bit of software. Even when I use the Sidekiq-provided Rails dashboard I don’t really feel like I am in control. This isn’t poking Sidekiq, it’s more poking my own brain - I’ve just never quite settled with it nor spent the time to understand it better.

I have had most success with que and deployed it for over 5 years and it has dilligently processed jobs. Over various projects I will have seen Que process millions of jobs (okay, not “Web scale”, but realistically not many of us are working beyond this scale). Que is still great, but for new projects I have most recently started to use GoodJob.

A note on GoodJob

This project is incredible (Postgres only). Absolute first-class ActiveJob integration, it is inspired by Delayed::Job and Que (see above). I am actively migrating Sidekiq backed queues to GoodJob because:

  • It is more comfortable/familiar to me to look at Postgres for job management and I would rately exceed a million jobs per day
  • The name is brilliant
  • ActiveJob adapter; well established, proven
  • Lovely built-in dashboard that shows worker activity
  • Built in scheduling
  • Job batching (this is in Sidekiq Pro) to very easily fan out/saturate the queue with jobs
  • Built in deconflicting/duplicate-prevention/concurrency control; basically what you need to stop running the same job with the same arguments at the same time/in quick succession

The elephant in the room (not the Postgres elephant!) is Basecamp’s recently released Solid Queue. I’ve no doubt this is a great Active Job backend and fully expect many people to gravitate towards it. Equally 37s/Basecamp will be pretty happy with it to be moving their apps to it. However at the time of writing the README has this snippet:

Improvements to logging and instrumentation, a better CLI tool, a way to run within an existing process in “async” mode, unique jobs and recurring, cron-like tasks are coming very soon.

Lack of in-process async doesn’t bother me too much, but the other parts are pretty important to me so I will continue to use GoodJob. Ben Sheldon, the author of GoodJob, wrote about Solid Queue late 2023, an it’s worth a read. GoodJob was referenced heavily when they built Solid Queue.

Deployment

Now exclusively Hatchbox. There is clearly key person risk in the platform, but it’s been incredible in building load-balanced, resilient apps.

I find Kamal intruiging but I lack the energy to learn something new at the moment. I spent a lot of time Dockerizing/productionising a Rails app and I’m more than happy with Hatchbox’s push-to-deploy.

Monitoring

For production now exclusively Sentry. Recently tried AppSignal and liked it (automatic metrics on Sidekiq workers, servers), but lacked Pushover support which is important to me. I’m used to Sentry and don’t want to innovate here too much.

I’ll use pgHero to keep an eye on Postgres performance. If something is getting out of hand I’ll paste the explain plan into pgMustard and almost always just do what it tells me (unless it’s a write heavy setup).

Of course there are more Gems….

The above is what forms the core of a new Rails application in 2024.

Other mentions would go to…

ahoy / ahoy email
discard
dotenv
fast_jsonparser
httparty
kramdown
lograge
noticed
pagy