Cursor Is Breaking Your Rails Migrations (Here’s How to Fix It)

AI-assisted development tools like Cursor and Claude Code are quickly becoming part of everyday Rails workflows. They’re great at scaffolding code, speeding up repetitive tasks, and helping you stay in flow. Used well, they can significantly improve productivity and reduce the time spent on boilerplate.
But just like any tool, they come with trade-offs.
In our case, this caused a staging deployment to fail when migrations ran out of order, leaving the application expecting a table that hadn’t been created yet.
This post covers a real-world issue our team ran into using Cursor with Ruby on Rails--specifically around migrations and model generation-and how we fixed it using Cursor rules.
The Problem: Broken Rails Conventions
# db/migrate/20240502000001_create_users.rb
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :email
t.string :name
t.string :encrypted_password
t.boolean :active, default: true
t.timestamps
end
end
endAt first glance, this looks completely fine. The migration runs, the table is created, and nothing appears broken.
But there are two underlying issues here.
1. Migration Timestamp Issues
Cursor often generates migrations with incorrect or static timestamps, such as:
20240502000001_create_users.rbInstead of using the current timestamp, it inserts an outdated value.
Why this is a problem
Rails uses timestamps to determine the execution order of migrations. If a migration is generated with an older timestamp:
- It gets inserted into the middle of existing migrations based on its timestamp.
- Execution order becomes inconsistent
- Schema state can become unpredictable
- Deployments may behave differently across environments
- New developers may encounter failures when setting up the project
This isn’t just untidy - it introduces real risk.
2. Incorrect Generator Usage
Cursor also tends to generate tables using:
rails generate migration CreateUsersInstead of:
rails generate model UserWhy this matters
Using the migration generator directly bypasses important parts of the Rails workflow:
- No model file is created
- No RSpec files are generated
- No FactoryBot factories are created
- Naming conventions can drift
You end up either doing manual cleanup or missing pieces entirely. Over time, this leads to inconsistency across the codebase.
The Importance of Rails Conventions
Rails is built around convention over configuration. The ecosystem assumes a predictable structure, and most tools - RSpec, FactoryBot, linters - build on top of that.
When those conventions are broken, even in small ways, the impact compounds:
- Migration history becomes harder to reason about
- Environments drift apart
- Bugs become harder to trace
- Onboarding new developers gets more difficult
AI tools don’t understand your project’s workflow - they optimise for “valid code,” not “correct behaviour.”
The Solution: Cursor Rules
Cursor supports project-level rule files that guide how it generates code. These rules live inside a .cursor directory and are automatically included in its context.
Think of them as guardrails that keep the AI aligned with your conventions.
Step 1: Create a Migration Rule File
Create the following file:
.cursor/rules/migration-creation.mdcAdd:
---
description: Rails migration creation conventions
globs: db/migrate/*.rb
alwaysApply: false
Rails Migration Creation Rules
1. Timestamp Integrity
- Always generate migrations with a timestamp greater than the latest existing file in
db/migrate.
- Before creating a migration, inspect the current latest timestamp.
- Never generate out-of-order migrations.
- Do not use hardcoded or static timestamps.
2. Table Creation
- When creating a new table, always use:
rails generate model ModelName
- Never use:
rails generate migration CreateModelName
- The model generator ensures:
- Proper timestamp ordering
- Model file creation
- Test file generation (RSpec)
- FactoryBot setup (if configured)
- Correct naming conventions
3. Naming Conventions
- Follow standard Rails naming:
CreateUsers
AddFieldToUsers
RemoveColumnFromOrders
- Use clear, descriptive names that reflect the change.
4. Safety Checks
Before generating any migration:
- Confirm no newer migration exists
- Ensure no naming conflicts
- Avoid duplicate or redundant migrations
- Prefer reversible migrations where possible
This ensures Cursor generates code that aligns with Rails conventions - not just syntactically valid output.
Step 2: Register the Rule
Create or Update:
.cursor/instructions.mdc
Then add the following inside it:
---
description: Core development guidelines for Winboard
globs:
alwaysApply: true
---
Project Name Development Guidelines
Specialized Rules
Area: Migration Creation Patterns
Rule File: migration-creation.mdc
This keeps your rules organised and makes it easy to expand later.
Conclusion
If you're using AI tools in a codebase, don't rely on default behaviour. Define your expectations early.
Start with migrations, but extend this approach to:
- Service objects
- Background jobs
- API patterns
- Testing conventions
Rails thrives on consistency. If you want the benefit of AI without compromising your codebase, you need to encode those conventions explicitly.
Cursor rules are a simple and effective way to do exactly that.
AI won’t follow your conventions unless you make them explicit.