So I can only speak to how I did it, but I have successfully migrated a rails monolith to an elixir monolith app, over the period of about a year and a half. So our stack at the beginning is nginx serving a static frontend, a rails json API, a MySQL database, and all of those are in docker containers inside docker-compose. Nginx is configured so any requests to /api
are proxied to rails. At the end, we ended up with nginx serving the frontend, a phoenix json api, raills API for some legacy accounts, and a postgres database. The way we decided to do this is by using the Terraform library. Requests to the api first hit nginx, are proxied to phoenix, and if phoenix doesn’t understand it then it’s sent to rails using the terraform library.
So the first steps was setting up terraform, making sure rails and phoenix had the correct database credentials, and then defining some basic ecto schemas. I started here by copying out of my schema.rb
file and pasting it into the ecto schema file, then changing the syntax so it matches ecto’s, which worked pretty well. Then I needed to configure the JWT library Joken so that it matched the same secret key and settings we were using with devise-jwt on the rails side. Now we can log a user in via rails and authenticate that person in elixir land. Yay, now it’s time for feature development!
The business case behind adopting phoenix for me was channels, so it makes sense that the first feature we shipped was a chat system. Then it was a slow process of new feature development happening in elixir, and any time I needed to fix a bug with some ruby code I tried to rewrite it in elixir. This was by far the longest step of the whole process.
The two lingering things in rails for a long time was image uploading, which we were doing in rails with ActiveStorage, and user registration/authentication, which we were doing with devise. To solve the image uploading issue, I used Waffle and waffle_ecto to upload any new images, and I dug into the activestorage source code to figure out how they were generating urls for the images, and I replicated that in elixir code so we could generate a URL in elixir based on the activestorage tables that were stored in the database. This was probably the only actually hard code to write in this whole process. I have considered open sourcing this, but I’m not sure if anything has changed in activestorage since I wrote that code, and I wouldn’t really want to maintain it as it’s kinda a one and done thing in terms of usefulness. This was with rails 5.3 at the time. So once we had that, it was basically just writing a function to check if there is a waffle uploaded image, if so use that url, and if not then call the activestorage url function. For passwords, I did something similar except using the phx_gen_auth
library to create the new password tables. When you change your password or create a new account, it uses the phx_gen_auth
code to set that. When logging in, it first checks if your password is valid with phx_gen_auth
, if that fails it calls rails and does the check with devise.
I also migrated mysql to postgres during this time, and while not strictly related I thought I’d share that story as well. Elixir made it super easy, I added both the mysql driver and the postgres driver to my dependencies, then I renamed my repo module which was set up to work with mysql to OldRepo
. Then I created a new repo module and configured that to work with postgres. I then wrote a function to call from IEx, which basically listed all the tables I wanted copied, and basically did Repo.insert_all(OldRepo.get(table))
, though I think I needed a bit more code than that due to the limits postgres has on the number of items you can insert at one time with insert_all
. All in all, pretty easy!
Hopefully this is what you were looking for!