Cursor Rules for Ruby on Rails: Models, Jobs, and Tests

Practical Cursor rules for Rails projects: application boundaries, migrations, background jobs, and testing, with fenbs recording the domain decisions and recurring lessons behind those rules.

Updated 4 min read

Cursor rules for Ruby on Rails tell a coding assistant how your application is structured, which patterns your models and services follow, how migrations must be written safely, and what a passing test looks like. Without them, an assistant writes correct Rails code that breaks your architecture: a model with too much logic, a migration that locks the table in production, or a test that hits the database when it should not. fenbs records the domain decisions and recurring lessons behind these rules, so the next session reads what the team has already settled before touching a model.

This page provides a working template for each Rails concern and explains the traps these rules prevent.

What Rails-specific Cursor rules cover

Rails has strong conventions, but large applications diverge from them as they grow. An assistant working from Rails defaults may produce code that is idiomatic Rails but wrong for your application. The areas that most often need explicit rules are: where business logic lives, how models call each other, how migrations are written for a live database, how background jobs are structured, and which RSpec patterns the project uses.

  • Application boundaries: where models, services, and domain objects are responsible for each type of logic.
  • ActiveRecord constraints: which callbacks, scopes, and associations are acceptable and which are not.
  • Migrations: how to write a migration that runs safely on a large table in production.
  • Background jobs: how jobs are named, which queue they use, and how retries are handled.
  • Testing: which test doubles to use, when to hit the database, and which factories the project uses.

Application boundary and model rules

Fat models are the most common Rails technical debt problem. When an assistant without boundaries adds a new method to a model, it usually adds it to the closest model rather than the right object. A rule that says where different kinds of logic belong saves the decision being made implicitly on every new method.

Example application boundary rules for .cursor/rules/rails-architecture.mdc
# Rails application architecture

## Where logic belongs
- Models: data validation, associations, scopes, and simple queries. No service calls from callbacks.
- Service objects in app/services/: multi-step operations that involve more than one model.
- Domain objects in app/domain/: business logic that does not need ActiveRecord.
- Presenters in app/presenters/: view formatting; do not put these in models or controllers.

## Model rules
- Callbacks (before_save, after_create) are acceptable for data normalization only.
  Do not use callbacks to trigger side effects such as sending emails or queuing jobs.
  Use service objects for those.
- Scopes should be chainable and return ActiveRecord::Relation.
  Do not write a scope that returns an array.
- Do not add a belongs_to without adding the matching has_many or has_one on the other side.

## Controller rules
- Controllers call one service or model method per action.
  A fat controller action is a sign the logic belongs in a service.
- Permit params explicitly with strong parameters; do not use permit!.

Migration rules

A migration that adds a NOT NULL column without a default, or one that calls add_index without algorithm: :concurrently, will lock a large production table. These mistakes are not obvious from the migration code, and an assistant does not know the size of your tables. A rule that describes safe migration patterns for your deployment is one of the most valuable rules in a Rails project. Migration rules also serve as documentation: when a new developer or assistant needs to understand why a backfill job exists alongside a migration, the rule that required it explains the reason. Record the row count threshold that triggers the backfill requirement in the rule itself so the decision is visible next time a migration affects a large table. When the team upgrades PostgreSQL, review the migration rules against the locking behavior documented for the new version; what required a concurrent index in an older release may be handled differently in a newer one, and rules that reference an old workaround become misleading if they are not updated.

Example migration safety rules
# Rails migration rules

## Locking risk
Adding a column with NOT NULL and no default locks the table in most PostgreSQL versions below 11.
For tables over 1 million rows, always add a nullable column first, backfill, then add the constraint.

## Index creation
Use add_index with algorithm: :concurrently for large tables:
  add_index :orders, :customer_id, algorithm: :concurrently
Never add a concurrent index inside a transaction. Concurrent index creation requires
  disable_ddl_transaction!
at the top of the migration.

## Backfill migrations
Do not backfill more than 10,000 rows in a single migration.
Use a background job or a data migration gem for large backfills.

## Rollback safety
Every migration must have a down method or use reversible.
Destructive changes (drop_column, drop_table) require a separate migration and a deployment plan.

Background job rules

Jobs that are not idempotent will corrupt data if they run twice, which happens on every retry. An assistant that writes a job without idempotency in mind creates a latent problem that only appears under load or after a network error. The rule needs to make idempotency an explicit requirement.

Example background job rules
# Background job rules

## Job naming
Name jobs as nouns: SendInvoiceEmailJob, ProcessRefundJob. Do not use verb phrases.
Place job files in app/jobs/. One file per job class.

## Queue assignment
Default queue: default.
Email jobs: mailers.
Long-running imports or exports: low_priority.
Do not introduce a new queue name without adding it to config/sidekiq.yml.

## Idempotency
Every job must be safe to run more than once with the same arguments.
Guard against duplicates with a database unique index or an idempotency key.
State this explicitly in the job's class-level comment.

## Retries
Use Sidekiq's retry count. Default is 25 retries.
For jobs that must not retry (such as sending a one-time code), set sidekiq_options retry: 0.
Do not rescue all exceptions silently; let Sidekiq handle retries unless a specific exception
is expected and safe to ignore.

Testing rules

RSpec gives a lot of flexibility in how tests are written, which means teams diverge quickly without a rule. An assistant may use let! where let is sufficient, hit the database when a double would be faster, or create fixtures instead of using the project's factory setup. Rules that name the preferred patterns make the test suite consistent.

Two areas need special attention in Rails test rules. The first is factory strategy: an assistant that defaults to create in every test produces a test suite that is slow to run and relies on database state. The second is subject and it descriptions: Rails projects often accumulate tests that say "returns true" or "is valid" without describing what scenario is being tested. A rule that requires human-readable context strings catches both problems at the point of writing rather than at code review.

Example RSpec rules
# RSpec conventions

## Factories
Use FactoryBot. Factories are in spec/factories/.
Use build_stubbed when the object does not need to be persisted.
Use build when associations must exist but persistence is not needed.
Use create only when the test requires database interaction.

## Test doubles
Use instance_double and class_double for type-checked doubles.
Do not use allow_any_instance_of; find the specific instance instead.

## Matchers
Prefer eq over == in expectations.
Prefer have_attributes over manually checking each field.
Use change matchers for side-effect assertions:
  expect { action }.to change(Order, :count).by(1)

## Context and description
Describe methods with # for instance methods and . for class methods.
Context blocks describe state: 'when the order is paid', 'with a missing email'.
How fenbs helps

Keep domain decisions and recurring mistakes in one place

fenbs records the decisions behind your Rails rules and the lessons from recurring mistakes. When the team decides that callbacks must not trigger side effects, record it as a standing rule in fenbs. When a migration causes a production issue, record it as a lesson. The next Cursor session reads those before touching a model or writing a migration.

Create a free account

Free for one person · No credit card · Works with Claude, ChatGPT, Cursor and more

How to do this with fenbs

  1. Create a free account and add a project for your Rails application.
  2. Record the application boundary decisions in fenbs as standing rules: where service objects go, when to use callbacks, how migrations must handle large tables. Link each rule to the reason.
  3. When a migration or job causes a production incident, add a lesson in fenbs. Describe what happened, what the rule should have been, and what the fix was. The next assistant reads lessons before starting similar work.
  4. Connect Cursor to fenbs over MCP from Integrations. Ask it to read the project context, including rules and lessons, before working on models, migrations, or jobs.
  5. Review the fenbs rules when the Rails version changes. Upgrade decisions belong in the task record, and any rule that changes with the upgrade should be updated in fenbs and in the Cursor rule files together.

Questions people ask.

Should I have one Cursor rule file or several for a Rails project?

Several files scoped by area work better than one large file. A file for architecture rules, one for migration rules, and one for testing rules each stay focused and are easier to update independently. Use the glob pattern to attach the migration rules only when a migration file is open.

How do I handle rules that depend on the Rails version?

State the Rails version in the rule file itself, for example as a comment at the top. When you upgrade, update the version and any rules that changed. If a rule applies from Rails 7.1 onward but not earlier, say so. Keeping the version explicit prevents an assistant from applying rules designed for an older version.

Can Cursor rules help with Rails upgrade migrations?

Yes. Write a temporary rule file describing the upgrade steps and the deprecated patterns to replace. Once the upgrade is complete, remove the temporary rules and update the permanent rule files to reflect the new Rails version. Record the upgrade decisions in fenbs so the reasoning is available later.

What is the most dangerous Rails mistake an AI assistant typically makes?

Writing a migration that adds a NOT NULL column without a default on a large table is the most common dangerous mistake. It looks correct in development and in testing, but locks a production table for seconds or minutes depending on row count. A clear migration safety rule prevents it.

More in Prompts and skills for AI work

All Prompts and skills for AI work articles

Start with one thing.

Free for one person · No credit card · Works with Claude, ChatGPT, Cursor and more

There is nothing to set up first. Write one line and you’ve started.