Using AI to convert from Rails to Phoenix?

I have a fairly old Rails project that is non-trivial but also not a beast, and I would like to convert it to a modern Elixir app, retaining the database structure and (at this point) the existing UI. LiveView would be nice but it would be perfectly fine for now to use regular controllers.

Since AI is pretty good at working from a spec, I’d like to try and use one of the major AI tools (Claude, Codex, Jules, …) to do this. It seems that the key would be to do this step-by-step and keep the context as focused as possible. For example, start with the database migrations, relationships, then validation, model operations, etc.

Has anyone done this? Any gotchas or recommendations?

Thank you!

Mike

2 Likes

This is a cool idea! Even something that can generate migrations or the initial files from a Rails schema could be really handy and make a port that little bit easier.

1 Like

Currently knee-deep in moving a Rails 2.3 app (on MySQL) over to Phoenix with LiveView + PostgreSQL. This app has zero specs. Nada. Zilch. But it’s been quietly doing its thing for the client for the last 15 years without many hiccups. So, I’m basically doing surgery on a patient that’s been healthy for a decade and a half. The reason for the operation is that it’s getting harder to keep the old JS libraries like Prototype limping along, and the customer recently took a hit when they were forced to migrate from MySQL 5.7 to MySQL 8. That alone required a big lift to move Rails 2.3 over to Rails 2.3 LTS, and they don’t want to go through another “fire drill” like that ever again.

Didn’t want to just throw the entire codebase into Claude or ChatGPT and pray, so I’ve been breaking the migration down into chunks:

  1. Dumped the MySQL DB schema → fed it to AI to get a Postgres version, plus suggestions for cleaner field types and future-proofing.
  2. Logged all the field type changes, then used draw.io to map out how the tables actually connect so I could decide which models to tackle first.
  3. Spun up a barebones Phoenix + LiveView app and scaffolded those models in priority order.
  4. Right now: pulling in the old business logic one model at a time, while getting AI to crank out tests for each bit as I go. This part is equal parts archaeology and translation work. Finding ancient Rails patterns and figuring out their shiny new Elixir equivalents.
  5. Next up will be staging for the client’s management to kick the tires.
  6. Then a tiny rollout to 1–2 locations for a couple months of feedback.
  7. Expand to ~25 locations for another few months of tweaks.
  8. Finally, the big switch for all 100+ locations.

One extra wrinkle: most of the production data from MySQL ultimately ends up in ClickHouse for warehousing. As I launch these new locations, I plan on setting up triggers in Postgres so it will automatically dump the exact same data format for the warehouse into a temp table that I sync daily. This way, the downstream analytics won’t even notice the migration happened. Once the rollout is solid, the next big mountain will be refining and transforming that warehouse data.

8 Likes

This is super interesting, thank you! Did you try anything that simply didn’t work (like maybe converting the relationships?) and when was this all happening (ie: how good was the AI compared to today).

1 Like

While you are at it, I strongly recommend skipping the more raw Ecto stuff and just use Ash. You declaratively describe stuff and it can even generate the migrations for you, though it can do much, much more than that. It’s a joy to use and allows you to compartmentalize stuff a la Phoenix contexts but IMO better.

3 Likes

First of all, I don’t consider myself an expert in Elixir, but as an intermediate Ruby dev, I know enough about programming in general to wiggle my way around. I purchased Pragmatic Studio’s Elixir OTP and Full-Stack Phoenix courses last year and have been using them as my foundation to tackle this challenge. I’ve been at this for the past 3 months, so everything is from a fairly recent experience. Overall, Claude is my go-to…specifically Sonnet 3.7, which seems to perform better overall. As for snags, you can’t expect AI to take OOP patterns and automagically convert them into functional and/or concurrent counterparts. I’ve noticed that this part definitely requires some back-and-forth with the AI to get right.

This sounds very interesting. I’ll have to take a look at Ash. Thanks for the tip :+1:

Thanks for pointing me to Ash…boy, it’s hell of a framework…mind blown!!! I’m working on a new new point-of-sale project and this will most definitely come in handy.

2 Likes

I was a skeptic for a long time but as it usually happens in life, I was forced to use it to appreciate it. I grumbled about it, of course, like every self-respecting aging techie, but came to appreciate it a lot.

Best feature is being able to describe one resource’s data model once and then reuse that for JSON API, GraphQL, storage backends like PostgreSQL / sqlite3, and others. Also before_action / after_action, notifiers, PubSub… The authors did an amazing job.

Feel free to ask about how to do this and that. I am not an expert by any stretch but the parts that I learned I know pretty well. Busy as all hell too but I stop by in the forum 1-2 times a day.

4 Likes

So I have wondered about the exact same thing. I haven’t done it yet, but I have had success upgrading an Elixir app. The upgrade included:

  • Phoenix 1.4 to 1.8 with no shims or backwards compatibility tricks, including
    • EEX → HEEX
    • LEEX → HEEX
    • View → HTML
    • Routes → ~p()
  • LiveView 0.16 to 1.1 including
    • Move to new components.ex
    • Streams
    • Async assign
  • Bootstrap 4 → Tailwind + DaisyUI
  • Webpack → esbuild

One key thing that didn’t work was incremental upgrading. I first tried to take Phoenix and Liveview, feed in the CHANGELOG and UPGRADING guides for each version bump and work my way to 1.8/1.1 while going fully idiomatic for each version. I made it through a few of tech changes listed above, but got in a hot mess half way through trying to get the exact magic mix of packages right for each era of the upgrades. I eventually decided that a single-jump rewrite was going to take less time (and it did).

So I threw all the progress away from above, reverted back a clean old working branch. Then I did a mix phx.new in a parallel directory. I was using Warp which was a surprising capable AI codegen tool (and under appreciated). I mounted both the existing app and the newly generated app and described how I wanted to upgrade the one into a modern skeleton. Crucially, what worked for me, was keeping an up-to-date UPGRADE.md file in the new folder. It did a fantastic job coming up with a high level plan which was basically to start with copying the migrations and the business logic over and then building the interface up route by route.

As it started each route, it did a great job of understanding the core idea of each live/dead view it was replacing. But also, I think the Ecto Schemas were likely the most important source of truth it needed to create the views. I would get it then to add detail to each part of the high level plan as it went through each section making a very detailed plan in the end.

It took about 10 hours to migrate something that took me 3 months to write. The entire _web folder and all tests are from scratch. This is approach I’d take with migrating a Rails app too. If it was sufficiently complex, I may add a few extra “understanding” stages before even creating the plan file.

On process, I am a huge fan of this “create a markdown plan file as you go” approach. I suspect Claude Code, Cursor and friends are basically doing a similar thing under the hood now, but I like being able to carry my plan across sessions, LLMs and agents. I’ve also found that using Opus for planning is worth it, but Sonnet good enough for implementing the plan.

2 Likes

One thing I might recommend before doing a full conversion - you may want to first have AI make your rails app more convertible. This would mean:

  • Add more documentation to the Rails app
  • Adding more test coverage / integration tests to the Rails app

That way, the conversion process will have additional context on the behavior of the Rails app and the AI can also be instructed to port the integration tests over to Phoenix.

Hope that helps!

That’s a very good point. AI works best if it has access to a good feedback cycle – funnily enough that’s also useful to us humans – so a testsuite, which can at the same time run against both systems is great when porting from a to b.

Yeah, the AI did a great job explaining how the Rails app works and is structured. Regarding tests, I think I’d rather spend that time writing good tests for the Elixir version, but I see how it could be useful.

Thanks for sharing that, and wow, that was quite an upgrade! So far, the more automated approaches have not worked very well, and I am much more comfortable using the AI to present what’s in the Rails app in a more summarized form the the Rails source code. I can then use that to instruct the AI to make a new model etc. and do a bunch of work for me, but in chunks that allow me to carefully review everything and essentially save some typing.

When it comes to writing code or fixing bugs, I feel that agents today, Claude Code, Codex, Cursor, etc. definitely feels like a reckless new developer with the personality of a bullshitter, so I feel the need to keep a close watch. But in terms of analyzing things, explaining, finding stuff on the web, I’ve been very pleased with their work.

I have been keeping the summary documents in a separate folder, but I really like the idea of seeing UPGRADE.md in a project folder, so it is right there with the source history.

I also decided to use Ash (thanks for the extra nudge, @dimitarvp!) and I am happy with that decision. The biggest hurdle (so far!) was Ash Authentication. I like to really control the onboarding experience (the user’s first impression is super important, I think) and I didn’t like the default UI but it was tricky to figure out how to implement custom UI elegantly.

Thanks everyone for all the amazing sharing! It has been super helpful and I will keep sharing the journey here.

1 Like