Hi there! Our Ruby on Rails projects are managed by a group of devs, and the team will grow in the near future. As such, we’re looking into ways to make the onboarding experience easier, and reduce the amount of “works on my machine” problems as much as possible.
Therefore, we’re wondering what other people/organizations/companies are using for their Rails projects in this regard. Our current history w.r.t. this is as follows:
1. ad-hoc: No versioning at all. Just use what is on your machine.
Has all the problems you expect with bugs and developer confusion caused by incompatible versions between tools.
2. Use rvm
/rbenv
to manage Ruby, nvm
to manage NodeJS
(at the time of Rails 6 with Webpacker/sprockets, and even now that Rails 7 is a thing we need to rely on some NPM deps).
While this works, it requires people to manage these tools separately. They each come with their own way of installing and managing versions.
3. Use asdf to mange Ruby, NodeJS and Postgres.
This way there was one unified way to manage the versions of our tools. This improved the dev experience quite a bit. However, there still are many gems or NPM deps that require some native tool to exist (like ImageMagick, Selenium, libXML, OpenSSH, etc.). Onboarding and managing deps still is not as simple as “run the installer once” because issues will surely crop up and what exactly they are changes over time.
(Aside: we switched from SQLite to Postgres also in dev to reduce problems we had with incompatibilities between the two DBs that were hurting us when moving code between dev and production (such as handling of Decimal rounding, foreign key constraints and transaction guarantees.)
4. Use a Dockerfile to manage a unified “dev machine”
This made sure that we are all using 100% the same versions of all build tooling etc. However, there still are two glaring problems with this setup:
- Sychronization of source code between the host computer and the running docker image is slow, especially on mac. This is very annoying and slows down the development process.
- Maintaining a docker-based setup is quite complex. Currently, the docker image has a very low bus-factor in our organization (essentially I’m the only person that truly understands what is going on). It has also happened multiple times that the base image changes unexpectedly or (now that we have locked its versions down as much as is possible) needs to change; updating this thing is quite a challenge.
5. Using Nix and Bundix
We’re currently looking into using some build tools that specialize in reproducible builds. Nix is probably the most famous one (and the one we tried out), although there are others.
The nice thing about this setup, is that Nix (specifically, the sub-tool bundix) is able to read the gems from your Gemfile.lock and creates its own dependency DAG that includes all other non-Ruby build tools you might need. Nix guarantees that versions (and therefore compatibility of your thing) is always stable.
While nice in theory, current support for Nix – at least for Ruby apps – is still severely lacking:
- Installation takes a lot longer than simply running
bundler
andyarn
directly. - Gems with platform-specific binaries or versions are not supported. You need to build all the stuff from source.
- It will install all gems, including the ones only needed in other environments.
- While theoretically Nix can build docker images, it is impossible in practice: It builds everything from source and pull in all of the build tooling needed for that, and not only for the
production
gems but also for alldev
andtest
gems. All this stuff ends up in the final project or docker image.
So while we’ve tried quite a number of things now, we’re still looking for alternative solutions. What is your team doing in this regard?
Sincerely,
~Marten / Qqwy