Question

How to Drop Columns in a Rails Migration

rubyruby-on-railsdatabaseactiverecordrails-migrations

Question

I want to remove a column from a database table using a Rails migration. What is the correct Rails migration syntax for dropping a table column?

Short Answer

By the end of this page, you will understand how to remove columns in a Rails migration, when to use remove_column versus remove_columns, how reversibility works, and how to avoid common migration mistakes.

Concept

In Rails, migrations are Ruby files that describe changes to your database schema over time. When you want to delete a column from a table, you usually do it inside a migration so the change is tracked, versioned, and can be applied consistently across environments.

The core method for removing a single column is:

remove_column :table_name, :column_name, :type

For example:

remove_column :users, :age, :integer

Rails can also remove multiple columns:

remove_columns :users, :age, :nickname

This matters because database structure changes should be:

  • repeatable across development, test, and production
  • tracked in version control
  • reversible when possible
  • safe for teammates and deployment pipelines

Including the column type is especially useful for reversible migrations. If Rails knows what was removed, it has enough information to restore it during a rollback in many cases.

Mental Model

Think of a database table like a spreadsheet:

  • the table is the spreadsheet
  • each row is one record
  • each column is one field, like email or age

A Rails migration is like a set of instructions that says, “Update the spreadsheet structure.”

When you use remove_column, you are telling Rails:

Go to this spreadsheet, find this field, and delete it from every row.

That is why it is a structural change, not just a data change. Once the column is removed, the values stored in that column are gone too.

Take Quiz

Syntax and Examples

Basic syntax

remove_column :table_name, :column_name, :type

Example: remove one column

class RemoveAgeFromUsers < ActiveRecord::Migration[7.0]
  def change
    remove_column :users, :age, :integer
  end
end

This removes the age column from the users table.

Example: remove one column without type

class RemoveAgeFromUsers < ActiveRecord::Migration[7.0]
  def change
    remove_column :users, :age
  end
end

This may work when migrating forward, but it is less helpful for rollback because Rails may not know how to recreate the column.

Example: remove multiple columns

class RemoveOldFieldsFromUsers < ActiveRecord::Migration[7.0]
  def change
    remove_columns :users, :age, :nickname
  end
end

Safer reversible version

If you need more control, use up and down:

class RemoveAgeFromUsers < ActiveRecord::Migration[7.0]
  def up
    remove_column :users, :age
  end

  def down
    add_column :users, :age, :integer
  end
end

Use this approach when the migration is not automatically reversible.

Generating the migration

You can create a migration file with a command like:

rails generate migration RemoveAgeFromUsers age:integer

Then edit the file if needed and run:

rails db:migrate

Step by Step Execution

Consider this migration:

class RemoveAgeFromUsers < ActiveRecord::Migration[7.0]
  def change
    remove_column :users, :age, :integer
  end
end

Here is what happens step by step:

  1. Rails loads the migration file.
  2. It runs the change method.
  3. It looks for the users table in the database.
  4. It finds the age column.
  5. It executes SQL to drop that column from the table.
  6. The schema file is updated to reflect the new structure.

Before migration

idnameage
1Ana30
2Ben25

After migration

idname
1Ana
2Ben

The age values are removed because the entire column no longer exists.

If you rollback

If Rails has enough information to reverse the migration, it will try to recreate the column:

rails db:rollback

That restores the column structure, but not the original data unless you handled that separately.

Real World Use Cases

Removing columns is common when an application evolves.

Typical scenarios

  • Deleting unused fields

    • Example: nickname was added years ago but is no longer used anywhere.
  • Renaming by replacement

    • You create a new column such as date_of_birth, migrate data from age, then remove age.
  • Cleaning up old features

    • A feature flag or legacy setting is removed from the app and its database column is no longer needed.
  • Normalizing data

    • You move embedded information into a separate table and remove the original column.
  • Privacy and compliance

    • Sensitive information is no longer allowed to be stored, so the column must be dropped.

Example

A team originally stored full_name in one column, but later splits it into first_name and last_name. After backfilling the new data and updating the app, they remove full_name with a migration.

Take Quiz

Real Codebase Usage

In real projects, developers usually do more than just write remove_column.

Common patterns

1. Two-step deployments

A safe deployment often looks like this:

  1. Stop reading or writing the old column in application code.
  2. Deploy that code.
  3. Remove the column in a later migration.

This avoids breaking code that still expects the column to exist.

2. Guarding model code

Before removing a column, developers search for:

  • model validations
  • callbacks
  • scopes
  • serializers
  • views
  • API responses
  • background jobs

If any code still uses the column, production errors can happen after migration.

3. Explicit up and down

Teams often prefer explicit rollback behavior:

class RemoveLegacyTokenFromUsers < ActiveRecord::Migration[7.0]
  def up
    remove_column :users, :legacy_token
  end

  def down
    add_column :users, :legacy_token, :string
  end
end

4. Data migrations before schema removal

If old data must be preserved, teams first copy or transform it:

class MoveFullNameData < ActiveRecord::Migration[7.0]
  def up
    User.reset_column_information
    User.find_each do |user|
      next unless user.full_name

      parts = user.full_name.split(" ", 2)
      user.update_columns(first_name: parts[0], last_name: parts[1])
    end
  end
end

Only after the app uses the new structure do they drop the old column.

Common Mistakes

1. Forgetting the column type in a reversible migration

Broken or incomplete example:

class RemoveAgeFromUsers < ActiveRecord::Migration[7.0]
  def change
    remove_column :users, :age
  end
end

Why it is a problem:

  • Rails may not be able to reverse the migration automatically.

Better:

class RemoveAgeFromUsers < ActiveRecord::Migration[7.0]
  def change
    remove_column :users, :age, :integer
  end
end

2. Removing a column that the app still uses

Example problem areas:

  • user.age in views
  • validations like validates :age, presence: true
  • API serializers returning age

How to avoid it:

  • search the codebase before migrating
  • remove code references first
  • deploy in stages if needed

3. Expecting rollback to restore old data

Rollback may recreate the column, but it usually does not restore the deleted values.

How to avoid it:

  • back up data first if it matters
  • migrate important values into a new column or table before dropping the old one

4. Confusing remove_column with remove_columns

Use:

  • remove_column for one column
  • remove_columns for multiple columns

5. Removing columns directly in the database

Running raw SQL manually outside migrations can cause schema drift.

How to avoid it:

  • make schema changes through Rails migrations whenever possible
  • keep the schema file and migration history in sync

Comparisons

Related migration methods

MethodPurposeExampleBest when
remove_columnRemove one columnremove_column :users, :age, :integerYou are dropping a single field
remove_columnsRemove several columnsremove_columns :users, :age, :nicknameYou are deleting multiple fields at once
add_columnAdd a new columnadd_column :users, :age, :integerYou need to store new data
rename_columnRename a columnrename_column :users, :name, :full_nameThe data stays the same but the name changes
change_tableGroup table changes`change_table :users dot

change vs up/down

ApproachProsConsExample use
changeShorter and cleanerNot always reversibleSimple column removal with enough type info
up/downFull control over migrate and rollbackMore codeComplex or non-reversible schema changes

remove_column vs rename_column

If the column is still conceptually needed but has a bad name, use rename_column instead of deleting and recreating it. If the field is no longer needed at all, use remove_column.

Take Quiz

Cheat Sheet

Quick syntax

remove_column :table_name, :column_name, :type
remove_columns :table_name, :col1, :col2

Example

class RemoveAgeFromUsers < ActiveRecord::Migration[7.0]
  def change
    remove_column :users, :age, :integer
  end
end

Commands

rails generate migration RemoveAgeFromUsers age:integer
rails db:migrate
rails db:rollback

Rules to remember

  • Use remove_column for one column.
  • Use remove_columns for multiple columns.
  • Include the column type when possible for better reversibility.
  • Removing a column deletes its data too.
  • Rollback may restore the column structure, not the original values.
  • Check the codebase before dropping a column.

Safer explicit rollback

def up
  remove_column :users, :age
end

def down
  add_column :users, :age, :integer
end

FAQ

What is the Rails syntax for dropping a column?

Use:

remove_column :table_name, :column_name, :type

Example:

remove_column :users, :age, :integer

Can I remove multiple columns in one Rails migration?

Yes. Use remove_columns:

remove_columns :users, :age, :nickname

Do I need to specify the column type when removing a column?

It is strongly recommended. It helps Rails reverse the migration correctly.

Will rails db:rollback restore the deleted data?

Usually no. It may restore the column itself, but not the original values that were deleted.

Should I use change or up and down?

Use change for simple reversible migrations. Use up and down when you need explicit control over rollback.

Is dropping a column dangerous in production?

It can be if application code still uses that column. Remove code references first, then drop the column in a later deployment.

What is the difference between dropping a column and dropping a table?

Dropping a column removes one field from a table. Dropping a table removes the entire table and all its rows.

Take Quiz

Mini Project

Description

Build a small Rails migration that removes an outdated column from a products table. This mirrors a common real-world cleanup task where an application no longer uses a legacy field.

Goal

Create and run a migration that safely removes the old_sku column from the products table.

Requirements

  • Create a Rails migration for removing a column from products.
  • Remove the old_sku column.
  • Make the migration reversible.
  • Show the command used to run the migration.
Take Quiz

Keep learning

Related questions

View more