All jobs

Review Database Migrations in Django Pull Requests

An agent reviews Django migration SQL and exercises it on sandbox Postgres, returning PR findings about locks, data loss and rollback limits.

Run this on RushIn a Project that has your repository.

What the agent does

The agent reads the pull request's added migrations, dependencies and affected models before running anything. NetBox provides a concrete Django example: requirements.txt:2 pins "Django==6.1.2", pyproject.toml:11 requires Python ">=3.12", and docs/installation/1-postgresql.md:5–6 requires PostgreSQL 15 or later. netbox/netbox/configuration_example.py defines a PostgreSQL DATABASES entry and separate Redis task and cache settings. The review uses the project's actual dependencies and an isolated database configuration, never a developer's existing database or production connection.

Existing migration history provides useful comparisons without proving a new change safe. netbox/dcim/migrations/0227_alter_interface_speed_bigint.py alters the interface speed field to PositiveBigIntegerField, so a new type change deserves SQL inspection rather than a blanket assumption about widening columns. In core, 0025_add_job_execution_time.py adds a nullable duration field. The following 0026_populate_job_execution_time.py uses "atomic = False", a "BATCH_SIZE = 5000" and a historical model from apps.get_model. That split is relevant when reviewing a PR that combines a schema change with a long backfill.

The agent installs requirements, creates fresh Postgres and the Redis dependencies needed by the app, and runs Django management commands from the netbox directory where manage.py lives. It inspects python manage.py migrate --plan and python manage.py sqlmigrate for each changed migration, prepares the database at the PR's predecessor state, and applies the new operations. Representative synthetic rows exercise nulls, duplicate values and affected relationships where relevant. An empty-database migration pass alone cannot reveal data-dependent failures, table-size effects or production lock waits.

For a credible lock review, it separates the lock mode implied by generated SQL from lock waits actually observed in controlled concurrent sessions. It reviews transaction boundaries, backfill batching, index creation, constraint validation and reverse operations. RunPython requires inspection of the Python body as well as SQL output; a reverse no-op does not restore changed data. The agent tests rollback only against the disposable sandbox database, records data-loss cases and comments with a safer staged sequence. It leaves the PR's migration files unchanged.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Review migrations added by the identified open Django pull request; ask for its exact URL if the target is unclear. Read dependencies, models, migration history and project database setup. Use only fresh disposable sandbox Postgres and synthetic data, never production. Inspect migrate --plan and sqlmigrate, prepare the predecessor schema, apply the changed migrations and test representative data cases. Review generated SQL and RunPython bodies for table rewrites, lock scope, transaction duration, destructive or narrowing changes and rollback limits. Use controlled concurrent sessions where useful and distinguish measured waits from inferred risks. Comment on changed lines with evidence and a safe staged alternative. Return migration-review.md with commands, outputs and limits. Do not edit migrations or create another PR.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

The existing PR receives review comments for specific risky operations, accompanied by migration-review.md. The report lists changed migration names and dependencies, generated SQL, application and reversal results, synthetic data cases and any observed contention. Each finding distinguishes a destructive operation, an irreversible data transformation and a potentially disruptive lock rather than grouping all three under a vague warning.

A reviewer checks the proposed sequence against the deployment order and the live table's size, write rate and compatibility requirements. A successful sandbox apply proves that tested path on that dataset, not that the production migration has no downtime. Where safer work needs an expand-and-contract deployment, a separately scheduled backfill or a concurrent index, the comment explains that sequence and its transaction requirements. Actual production execution remains outside this review assignment.

Run it on every Django migration pull request

Have your PR webhook handler submit a session when a revision changes migration files. Replace the example repository resource with its exact pull-request URL and identify the base and head being reviewed. Use an Idempotency-Key specific to that revision so network retries reuse the same review. Your completion-webhook handler can collect migration-review.md and the session answer before deciding which findings need author attention.

The use case this job follows, with its automation and trigger: Review every pull request.

Why run it on Rush?

Migration review needs a real database, the correct predecessor schema and room to fail without harming an existing environment. Rush gives the agent its own sandbox for that work rather than asking it to experiment against your laptop's development data. It can retain the generated SQL, management-command output and synthetic-case results together so a review comment has evidence behind it.

Independent PRs can use separate runs within concurrency limits instead of sharing a local Postgres whose migration state changes underneath them. The recorded work makes the limit of each conclusion visible: source inspection, a fresh apply and a measured lock experiment are different evidence. Your team gets a review it can challenge and reproduce before authorizing any production database change.

Frequently asked questions

Does applying migrations to fresh Postgres prove there are no table locks?

No. An empty database can apply a blocking statement too quickly to expose its operational cost. The review inspects the SQL and transaction scope, adds representative data where useful and labels any controlled lock experiment separately from production behavior.

Can sqlmigrate explain a RunPython backfill?

Not by itself. The agent must read the Python operation, including historical model access, batching and reversal behavior, and exercise relevant cases. NetBox's execution-time backfill is a concrete example of why the Python body matters.

Will it automatically rewrite a dangerous migration?

The request is review-only and forbids editing migrations. A finding proposes the safe sequence and points to the risky operation. The author decides how to implement it in the PR.

Does a reverse function mean data loss can be undone?

A reverse operation may restore schema without restoring deleted or overwritten values. A reverse no-op explicitly performs no data restoration. The report evaluates what the reverse path actually does and records when recovery would require a backup or another forward migration.