Question
How to Drop Columns in a Rails Migration
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
emailorage
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.
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
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:
- Rails loads the migration file.
- It runs the
changemethod. - It looks for the
userstable in the database. - It finds the
agecolumn. - It executes SQL to drop that column from the table.
- The schema file is updated to reflect the new structure.
Before migration
| id | name | age |
|---|---|---|
| 1 | Ana | 30 |
| 2 | Ben | 25 |
After migration
Real World Use Cases
Removing columns is common when an application evolves.
Typical scenarios
-
Deleting unused fields
- Example:
nicknamewas added years ago but is no longer used anywhere.
- Example:
-
Renaming by replacement
- You create a new column such as
date_of_birth, migrate data fromage, then removeage.
- You create a new column such as
-
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.
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:
- Stop reading or writing the old column in application code.
- Deploy that code.
- 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,
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.agein views- validations like
validates :age, presence: true - API serializers returning
age
How to avoid it:
Comparisons
Related migration methods
| Method | Purpose | Example | Best when |
|---|---|---|---|
remove_column | Remove one column | remove_column :users, :age, :integer | You are dropping a single field |
remove_columns | Remove several columns | remove_columns :users, :age, :nickname | You are deleting multiple fields at once |
add_column | Add a new column | add_column :users, :age, :integer | You need to store new data |
rename_column | Rename a column |
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_columnfor one column. - Use
remove_columnsfor 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
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 when you need explicit control over rollback.
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_skucolumn. - Make the migration reversible.
- Show the command used to run the migration.
Keep learning
Related questions
How to Call Shell Commands from Ruby and Capture Output
Learn how to run shell commands in Ruby, capture output, check exit status, and choose the right method for scripts and apps.
How to Check Whether a String Contains a Substring in Ruby
Learn how to check if a string contains a substring in Ruby using include?, match, and multiline string examples.
How to Check if a Hash Key Exists in Ruby
Learn how to check whether a specific key exists in a Ruby hash using key?, has_key?, and include? with clear examples.