Julik Tarkhanov

Delete your old migrations, today

We get attached to code - sometimes to a fault. Old migrations are exactly that. They’re digital hoarding at its finest, cluttering up your codebase with files that serve absolutely no purpose other than to make you feel like you’re preserving some kind of historical record.

But here’s the brutal truth: your old migrations are utterly useless. They’re worse than useless - they’re actively harmful. They’re taking up space, they are confusing (both for you and new developers on the project), and they give you a false sense of security about your database’s evolution.

If your database is out-of-sync with schema.rb you need to solve that problem anyway, and - if anything - the migrations make that problem worse.

🧭 I am currently available for contract work. Hire me to help make your Rails app better!

Why your old migrations are completely useless

Let me repeat without mincing: your old migrations are not useful to you. I don’t care if they’re from 2010 or 2020 - they serve no purpose in your current codebase.

Here’s why:

You either run all of them or none. Your database schema has evolved beyond recognition. Those migrations that created tables with different column names, different constraints, different indexes? They’are likely to fail if the previous migration hasn’t succeeded. The foreign key constraints may break, the column types may be wrong, and you’ll spend more time debugging migration failures than it would take to just recreate your schema from scratch.

You’ll never run them from the beginning of history. This is the most delusional part of keeping old migrations - the idea that someday you might need to recreate your entire database from migration zero. When was the last time you actually did rails db:drop db:create db:migrate on a production system? Never, that’s when. You use db:schema:load or db:structure:load because it’s faster, more reliable, and doesn’t depend on the fragile chain of historical migrations.

And no, “keeping your migrations runnable from 0 to bring up the application” is not “clean engineering”. It is not a “good engineering practice”. It is bollocks. Just delete them.

Your environment will never match. Even if you wanted to run those old migrations, your Rails version has changed, your gems have changed, your database version has changed. The migration that worked perfectly in 2018 with Rails 5.2 and PostgreSQL 10 can explode in a spectacular fashion with Rails 7.2 and PostgreSQL 16. Moreover: migrations sometimes use code in the application. Yes, it is discouraged, and yes, you will have more than a few which will invariably use code from the application or gems (or Rails itself). That UserTagging model? Nowhere in sight. That neat acts_as_listicle? Out of maintenance and no longer available.

They are only useful for archeology. You have git for that.

How to delete your old migrations

First, make sure you commit your schema.rb or structure.sql. This is your source of truth now, not those ancient migration files.

# Make sure this is up to date
rails db:schema:dump
git add db/schema.rb
git commit -m "Update schema before migration cleanup"

Now, decide on a meaningful threshold. I recommend keeping migrations from the last 6-12 months, depending on how active your development is. Everything older than that is just digital clutter.

# Find migrations older than 6 months and delete them
find db/migrate -name "*.rb" -mtime +180 | xargs git rm

# Or be more specific - delete everything before a certain date
# Let's say you want to keep migrations from 2024 onwards
find db/migrate -name "2023*.rb" | xargs git rm
find db/migrate -name "2022*.rb" | xargs git rm
find db/migrate -name "2021*.rb" | xargs git rm

# ...and then
git commit -m "Delete old migrations - schema.rb is the source of truth"

The psychological barrier

I know what you’re thinking: “But what if I need to understand how the schema evolved?”

You won’t. You really won’t. If you need to understand the current state of your database, look at schema.rb. If you need to understand the history, look at your Git history. If you need to understand the business logic, look at your models and tests.

The migration files themselves are just implementation details of how you got from point A to point B. The important thing is where you are now, not the 47 different ways you tried to add a column before you got it right.

What about production deployments?

This is where people get scared. “But what if I need to deploy to a fresh production server?” If you will – everything is going to be topsy-turvy, you run rails db:schema:load or rails db:structure:load and presto! Your production database gets created from your schema file, not from running 200 migrations in sequence. A migration is a brittle piece of code, which relies on 2 things being stable - the entire state of your codebase and the entire state of your database. You want to minimize the amount of time where these 2 guarantees must be maintained.

The only exception

There’s exactly one scenario where you might want to keep old migrations: if you’re using a gem which installs its own migrations. They will still be recorded in your schema/structure, but when you run bin/rails some_gem_with_database_tables:update or bin/rails some_gem_with_database_tables:install it is likely that the gem will try upgrading its schema by outputting migrations into your source tree. It will also skip those migrations if you already have them (the generator will offer you a dialog for “overwrite/skip/merge”. You likely want to keep those.

Just do it

Your old migrations are digital hoarding. They’re taking up space, confusing people, and serving no purpose. Delete them today. No, they will not run if you try them now. And your AI agents will eat through mountains of tokens trying to make that migration from 2012 work.

Delete them. You’ll feel better. Your codebase will be cleaner. New developers will thank you. And you’ll never miss them. I promise.