2026, I always develop with coding agents: Keeping CI green, fixing warnings: with coding agents Issue reports with reproduction cases: with coding agents These take far less of my time Time for "important but not urgent" work 5
# ... self.fixture_table_names = (fixture_table_names | fixture_set_names).sort # test/test_helper.rb, generated by rails new # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. fixtures :all comments (child) loads before posts (parent) 9
update on table "comments" violates foreign key constraint "fk_rails_2fd19c0db7" DETAIL: Key (post_id)=(980190962) is not present in table "posts". So foreign keys are disabled while fixtures load 10
Needs superuser privileges, even for the table owner ERROR: permission denied: "RI_ConstraintTrigger_c_11520776" is a system trigger Not available on some hosted databases, and far more than an app needs 2. Rows loaded while disabled are not validated ENABLE TRIGGER ALL does not validate them 12
Make every foreign key DEFERRABLE , then SET CONSTRAINTS ALL DEFERRED Deferrable constraint: the check waits until COMMIT Table owner is enough: no superuser privileges needed Issue rails/rails#28888: fixtures fail with PG::ForeignKeyViolation t.belongs_to :connection, foreign_key: { on_delete: :restrict } Why: only the NO ACTION check is deferred; on_delete actions still act immediately Reverted before Rails 5.1.0 13
Normally checked per statement; this can wait until COMMIT NOT VALID / VALIDATE CONSTRAINT Whether existing rows have been checked NOT ENFORCED (new for foreign keys in PostgreSQL 18) If the constraint is NOT ENFORCED, the database system will not check the constraint. i.e. rows written by INSERT / UPDATE are not checked https://www.postgresql.org/docs/18/sql-createtable.html 14
fk_rails_... NOT ENFORCED; -- insert the fixture data ALTER TABLE comments ALTER CONSTRAINT fk_rails_... ENFORCED; Disables and re-enables the foreign key itself Solves both problems: Back to ENFORCED : existing rows are validated Table owner is enough: no superuser privileges needed 15
INT PRIMARY KEY, parent_id INT, CONSTRAINT child_parent_fk FOREIGN KEY (parent_id) REFERENCES parent(id) DEFERRABLE INITIALLY DEFERRED); ALTER TABLE child ALTER CONSTRAINT child_parent_fk NOT ENFORCED; ALTER TABLE child ALTER CONSTRAINT child_parent_fk ENFORCED; -- pg_trigger tgname | RI_ConstraintTrigger_c_25894 tgdeferrable | f (false) ← should be t (true) tginitdeferred | f (false) ← should be t (true) 17
database_version >= 18_00_04 end # postgresql/referential_integrity.rb (rails/rails#57378) def disable_referential_integrity(&block) if supports_enforced_foreign_keys? toggle_foreign_key_enforcement(current_schemas_only: true, &block) else # DISABLE TRIGGER ALL / ENABLE TRIGGER ALL, as before Older PostgreSQL: still DISABLE TRIGGER ALL , still needs superuser privileges 20
= "#{quote_column_name(fk["schema_name"])}." \ "#{quote_column_name(fk["table_name"])}" execute("ALTER TABLE #{table} " \ "ALTER CONSTRAINT #{quote_column_name(fk["constraint_name"])} " \ "#{enforced ? "ENFORCED" : "NOT ENFORCED"}") end One ALTER TABLE ... ALTER CONSTRAINT per foreign key The same SQL as NOT ENFORCED / ENFORCED shown earlier 22
ActiveRecord::Migration[8.2] def change add_foreign_key :comments, :articles, enforced: false end end # For general use, e.g. application code connection.change_foreign_key :comments, :posts, enforced: false connection.change_foreign_key :comments, :posts, enforced: true Public API for applications too, not just for fixtures (rails/rails#57377) 23
:users do |t| t.string :name t.timestamps end end end : which Rails version's behavior this migration follows Not the Rails version running it [4.2] 25
:posts, :published_at, :datetime end end -- Migration[6.1] ALTER TABLE "posts" ADD "published_at" timestamp -- Migration[7.0] ALTER TABLE "posts" ADD "published_at" timestamp(6) Rails 7.0 changed the default datetime precision to 6 Each migration keeps the behavior of its own version 27
8.1) class V6_1 < V7_0 class PostgreSQLCompat def self.compatible_timestamp_type(type, connection) if connection.adapter_name == "PostgreSQL" type.to_sym == :datetime ? :timestamp : type else type end One class per migration version, inheriting the next newer one Older classes adjust options for the old behavior, then call super Adapter differences: adapter_name string checks in the same class 28
Identity column: the SQL-standard counterpart of MySQL's AUTO_INCREMENT Migration[8.2] +: identity columns Older: sequences 2. Third-party adapters inheriting a bundled adapter e.g. the CockroachDB adapter, a separate gem for CockroachDB, a PostgreSQLcompatible database It should inherit the PostgreSQL adapter's migration compatibility 29
2026) First try: per-adapter modules included at runtime (rails/rails#57479) Modules got mixed across databases Along the way: @rafaelfranca suggested trying another pattern @kamipo simplified it: no extra state passed around in the options hash (rails/rails#59050) Not just coding agents: reviews from Rails core made it better too 32
class V6_1 < V7_0 def add_column(table_name, column_name, type, **options) type = :timestamp if type.to_sym == :datetime super end No adapter_name check: the class lives in the PostgreSQL adapter 33
= "p_" class AddIndexToPostsTitle < ActiveRecord::Migration[7.0] def change add_index :posts, :title end end Index name: index_p_posts_on_title on Rails 7.0, index_posts_on_title today Found by an agent; no user reported it since Rails 7.1 Fixing it breaks rollback of indexes created under the old name Left it out (rails/rails#58955 closed): what would fixing it break? 34
no code needed compatibility.rb deleted: yahonda/activerecord-cockroachdb-adapter@6cf2eaa Green on Rails main Started on my fork in the last few days, since rails/rails#58848 landed on Oct 13 38
privileges (PostgreSQL 18.4+) Per-adapter migration compatibility, tried with Oracle enhanced and CockroachDB Coding agents: rebuilt a design I couldn't crack in 2018 You decide what to do: try what you've always wanted to do 39