unsort_db_schema_columns
Restore the natural (database ordinal) column order in Rails schema.rb dumps.
Why this exists
In rails/rails#53281, Rails 8 changed ActiveRecord::SchemaDumper to sort table columns alphabetically. The intent was to reduce merge conflicts when two developers add migrations to the same table on parallel branches.
The downside is that schema.rb no longer reflects the actual column layout of the database. That breaks several real workflows:
db:schema:loadfor production parity. Since Rails 8 (#52830), a freshdb:migrateloadsschema.rbinstead of replaying migrations. With sorting on, freshly-loaded databases have a different column order than production databases built incrementally from migrations.- Postgres column-alignment padding. Postgres pads columns to alignment boundaries; deliberate column ordering (e.g. 8-byte → 4-byte → variable-length) can meaningfully reduce table size. Alphabetical sorting destroys any layout chosen for this reason.
SELECT *and bulk-import pipelines. Tools likepg_dump/mysqldumpround-trips,PG::Connection#put_copy_data, and CSV import/export depend on ordinal column position. Mismatched dev/prod column order breaks them.add_column :after/:before. Rails supports positional column options in migrations. With alphabetical sorting in the dump, those options have no effect on schema-loaded databases.- Gems that read
columns_hashorder. The Rails change affects in-memory ordering after schema reload, not just the file (e.g. it broke a label-picking heuristic in Bullet Train).
For full discussion, see PR #55414 (opt-in restoration), PR #56842 (full revert), and the original PR #53281.
Who should use this
You probably want this gem if any of these are true:
- You use
db:schema:load(or Rails 8'sdb:migrateon fresh DBs) to provision new environments and need it to match production column order. - You hand-tune column order in migrations for Postgres alignment-padding reasons.
- You have CSV import/export,
pg_dumpround-trip, orput_copy_datapipelines. - You use
add_column :after/:beforeand expect it to stick.
You probably don't need it if you're a small team where merge conflicts in schema.rb were the bigger pain than any of the above.
Install
# Gemfile
gem "unsort_db_schema_columns"
That's it. A Railtie auto-applies the patch when ActiveRecord loads.
To regenerate schema.rb in DB-natural order:
bin/rails db:schema:dump
Note: the gem only affects future dumps. The schema.rb already on disk is whatever was last committed. To get a correct natural-order schema.rb, run db:schema:dump against a database whose column order matches production (typically one built by replaying migrations, not loaded from a sorted schema).
How it works
Prepends ActiveRecord::SchemaDumper and overrides #table with a copy of the upstream method that omits the .sort_by(&:name) call introduced in PR #53281. A boot-time warning fires if loaded against an untested ActiveRecord major version, since the copied method body could drift.
Why only columns?
SchemaDumper also sorts tables, indexes, foreign keys, check constraints, and unique constraints. Those sorts have been in Rails for years (some since 2009) and this gem leaves them alone — only column ordering inside a table has functional consequences in the database engine (Postgres alignment padding, SELECT * ordinal position, pg_dump/put_copy_data round-trips, add_column :after/:before). Tables, indexes, and constraints are independent named objects; the order they're created has no effect on engine behavior, so sorting them in schema.rb is pure diff-stability with no downside.
Caveats
- Coupled to ActiveRecord 8.x's
SchemaDumper#tablemethod body. When you upgrade Rails, re-diff againstactiverecord/lib/active_record/schema_dumper.rband bump the version constraint here. - The merge-conflict problem #53281 was solving is back. That's the intended trade.
License
MIT.