note
hosted, paid — --remote
Redundant index — its columns are already a prefix of another index
Note — this works and blocks nothing, but a performance regression is likely.
What it catches
A B-tree index on (a) is fully covered by an existing index on (a, b): the planner can use the wider index for every query the narrow one serves. The extra index buys nothing and costs a write on every INSERT, UPDATE and DELETE, plus disk, plus vacuum time — forever.
Fires on
CREATE INDEX idx_orders_user ON orders (user_id);
CREATE INDEX idx_orders_user_created ON orders (user_id, created_at);Do this instead
Keep the wider index and drop the narrower one (CONCURRENTLY, in its own migration). If the narrow index is the one you need for a different ordering or as a UNIQUE constraint, say so with a different column order or UNIQUE — the rule stays silent on both.
-- Keep the wide index (user_id, created_at) — it serves both lookups.
-- In its own later migration, outside a transaction:
DROP INDEX CONCURRENTLY idx_orders_user;Catch this before it ships
This rule runs in the hosted service on Startup and above — add --remote with a team token, or use the GitHub Action: npx bolvrk check migration.sql