Comparison
Eugene vs pgfence
An honest comparison of two Postgres migration safety tools: Eugene (Rust, trace-oriented, single static binary) and pgfence (TypeScript, multi-ORM, with safe rewrite guidance and an LSP server).
What Eugene does well
Eugene is a focused Postgres migration safety tool written in Rust by Robin Kaaveland. It ships as a single static binary, is MIT-licensed, and the latest release is v0.8.3 (March 2026). Its signature feature is trace mode: Eugene runs your migration inside a transaction against a real PostgreSQL instance, observes the actual locks taken via pg_locks, then rolls back. That live verification eliminates a category of guesswork that purely static tools have to live with.
Its current rule catalog is 15 hints: E1 through E11 plus E15 (errors) and W12 through W14 (warnings). Eugene's lint mode is concise and well-documented, with one page per hint explaining the lock-level reasoning.
Feature comparison
| Feature | Eugene | pgfence |
|---|---|---|
| Language | Rust (single static binary) | TypeScript (Node.js native) |
| License | MIT | MIT |
| Latest release | v0.8.3 (2026-03) | v0.6.0 (2026-05) |
| SQL parser | libpg_query (Rust bindings) | libpg_query (Node.js bindings) |
| Rule surface | 15 hints (E1-E11, E15, W12-W14) | 36+ rules across DDL + policy categories |
| Input: raw SQL | Yes | Yes |
| Input: TypeORM / Prisma / Knex / Sequelize / Drizzle | No (raw SQL only) | Yes (built-in ORM extractors) |
| Trace mode (live lock observation) | Yes (single tx, BEGIN/observe/ROLLBACK) | Yes (Docker-based; observer polling for CONCURRENTLY) |
| Trace: catalog inspection | Yes (relfilenode diffs) | Yes (pg_class, pg_constraint, pg_index diffs) |
| Trace: ORM migrations | No | Yes (after ORM extraction) |
| Lock mode reporting | Yes | Yes (per statement, static + trace) |
| Safe rewrite recipes | Brief suggestions per hint | Step-by-step safe rewrites per rule (RULES.md) |
| DB-size-aware risk scoring | No | Yes (stats snapshot or direct connection) |
| LSP server / editor integration | No | Yes (LSP server + VS Code extension) |
| GitHub Action | No official action | Yes (composite action) |
| Output formats | Terminal, JSON, Markdown | Terminal, JSON, GitHub PR comment, SARIF |
| Coverage line in reports | No | Yes (analyzed vs. unanalyzable statements) |
| Policy checks | Some (lock_timeout via W14) | Yes (lock_timeout, statement_timeout, tx policy, app_name) |
| Memory footprint | Low (native binary) | Node.js process |
The trace-mode framing online is outdated
You will see older blog posts and threads claim that Eugene is the only Postgres migration tool with trace mode. That was true through 2025, but pgfence has shipped trace mode since v0.4. Both tools now diff catalog snapshots and report actually-acquired locks. The execution models differ: Eugene runs the migration in a single transaction (clean, but cannot observe CREATE INDEX CONCURRENTLY, which fails inside a tx), while pgfence spins up a disposable Docker Postgres and uses an observer connection to capture transient locks from CONCURRENTLY statements. If most of your dangerous patterns are concurrent index work, that distinction matters.
Where pgfence is honestly stronger
- ORM coverage: pgfence ships extractors for TypeORM, Prisma, Knex, Sequelize, and Drizzle. Eugene only reads raw SQL.
- Editor integration: pgfence has a first-party LSP server with diagnostics, hover, and supported quick fixes. Eugene has no LSP.
- Safe rewrite recipes: pgfence attaches a per-rule rewrite recipe (expand-backfill-contract sequences) directly in the output. Eugene gives shorter hint descriptions.
- Larger rule surface: 36+ pgfence rules vs. 15 Eugene hints, with explicit policy checks for missing timeouts and
CONCURRENTLY-in-transaction. - RULES.md catalog shipped in the package, browsable by editors and in-editor coding assistants.
- Outputs: SARIF for GitHub Code Scanning, GitHub PR comment markdown, JSON, terminal.
Where Eugene is honestly stronger
- Single static binary: no Node.js, no
node_modules, no version-pinned runtime. Easier to drop into a Rust shop or a minimal CI image. - Memory footprint: a native Rust binary uses far less RAM than a Node.js process. Matters in tight CI containers.
- Simplicity of trace mode: Eugene's single-transaction trace is conceptually simpler than pgfence's disposable Docker container plus observer connection. If you do not run
CONCURRENTLYpatterns, you may not need pgfence's extra machinery. - Hint catalog readability: Eugene's docs page per hint is clear and short; very pleasant to grep through.
When to choose Eugene
- You write raw SQL migrations only, no ORM in the pipeline
- Your stack is Rust or Go, and you do not want a Node.js dependency in CI
- You want static analysis plus simple live trace, and no
CONCURRENTLYpatterns - You prefer a single binary you can vendor or check into
tools/ - You actively dislike the JS package-management surface area
When to choose pgfence
- Your team uses TypeORM, Prisma, Knex, Sequelize, or Drizzle
- You want trace mode that handles
CONCURRENTLYstatements - You want step-by-step safe rewrite guidance in the output
- You need table-size-aware risk scoring
- You want a VS Code extension or any LSP-aware editor to surface diagnostics inline
- You want SARIF output for GitHub Code Scanning annotations
- Your stack is Node.js/TypeScript and a JS-native tool fits the toolchain
See also
- Squawk vs pgfence: Rust SQL linter that grew into a full SQL IDE
- strong_migrations vs pgfence: Ruby/Rails runtime interception
- pgrubic (github.com/bolajiwahab/pgrubic): a much larger Python-based lint surface (100+ rules), worth a look if rule count is your primary axis
- Atlas (atlasgo.io): schema-as-code with its own migration linter; different category but adjacent