Enable the Rails 7.2 framework defaults - #916
Open
suttondemlong wants to merge 2 commits into
Open
Conversation
This was referenced Sep 9, 2026
Bumps `config.load_defaults` to 7.2 and retires config/initializers/new_framework_defaults_7_2.rb, which #907 shipped with every option commented out. Dumped the affected settings before and after rather than reading the release notes. `load_defaults "7.2"` sets exactly five things: yjit false -> true active_job.enqueue_after_transaction_commit :never -> :default active_record.postgresql_adapter_decode_dates false -> true active_record.validate_migration_timestamps false -> true active_storage.web_image_content_types unset -> unset Four of the five are inert here: - `yjit` does nothing until the Ruby bump. The initializer is guarded by `defined?(RubyVM::YJIT.enable)`, and `enable` arrived in Ruby 3.3; on 3.2.3 the constant exists but the method does not. It starts mattering in PR 6. - Active Storage is not loaded -- the require is commented out in config/application.rb -- so `respond_to?(:active_storage)` is false and Rails skips that line entirely. - `postgresql_adapter_decode_dates` only affects queries carrying no type information. The one raw query in the app, `Food.fts`, runs through `find_by_sql`, which casts through the model's attribute types, and selects no date columns in any case. - `validate_migration_timestamps` raises on a migration dated more than a day ahead of now. The newest migration here is from October 2017. That leaves `enqueue_after_transaction_commit`, which is a real change: `:never` enqueued immediately even inside an open transaction, and `:default` asks the adapter. Sidekiq answers true -- both its own adapter and the one still shipped in activejob define it that way, so it does not matter which wins the load order -- meaning a job enqueued inside a transaction now waits for the commit, and a rollback drops it rather than leaving Sidekiq holding a job whose rows were never written. No existing call site changes behaviour. Active Job is reached from exactly three places: `TrackableUsage`'s `after_commit`, which by definition runs with no transaction open; `DataExportSchedulesController#create`; and six `deliver_later` calls, all of them inside Sidekiq workers or rake tasks. None sits in a transaction. Everything else in app/jobs is `perform_async` on a plain Sidekiq worker, which never enters Active Job. So this is protection for code not yet written, which is why it comes with a spec of its own. The suite is unaffected for a reason worth recording: DatabaseCleaner's :transaction strategy opens its wrapping transaction with `joinable: false`, and `ActiveRecord.after_all_transactions_commit` skips non-joinable transactions, so a top-level `perform_later` in a spec still enqueues immediately. Had that wrapper been joinable, every job spec would have been waiting on a commit that never comes. Also settles the deploy-ordering question the upgrade plan flagged for this step: none of the five options touches an on-disk format. `active_support.cache_format_version` is 7.1 before and after -- 7.2 does not change its default, and config/application.rb pins it explicitly anyway -- so there is no old-process/new-process cache incompatibility and this needs no special rollout. Verified: 327 examples, 0 failures; standardrb, erblint and zeitwerk:check clean; boots in development, test and production. The three new specs were checked in both directions -- on `load_defaults 7.1` the two transaction examples fail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`config.active_record.attributes_for_inspect` is a 7.2 addition that `load_defaults` does not set; Rails puts it in the generated production.rb instead, and #907 deferred it here on the grounds that it changes behaviour and so belongs with the defaults flip. It is worth taking rather than skipping. `inspect` on an Active Record object renders every column, and that string is how attribute values reach logs and Bugsnag reports -- on this app those values are health data. With `[:id]`, `Profile.new(birth_date: ...).inspect` is `#<Profile id: nil>` instead of a line carrying a date of birth, a sex, a time zone and a screen name. `full_inspect` still prints everything for when you want it. One exception, checked rather than assumed: `User` is unaffected, because Devise::Models::Authenticatable overrides `inspect` and wins the method lookup. Devise's version filters through `serializable_hash`, so credentials were never in that output, but the remaining columns still are. Every other Active Record model in the app takes the new behaviour. Development and test keep the `:all` default, where the whole point of `inspect` is to see the record. Verified: booted RAILS_ENV=production and confirmed `ActiveRecord::Base.attributes_for_inspect == [:id]`, `Profile#inspect` redacted and `Profile#full_inspect` complete. 327 examples, 0 failures; standardrb clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
suttondemlong
force-pushed
the
chore/rails-7-2-defaults
branch
from
September 10, 2026 00:01
d8b1017 to
401d446
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Continues the upgrade plan from #907, which landed Rails 7.2.3.2 with
config.load_defaultsstill at 7.1 andnew_framework_defaults_7_2.rbshipping with all five options commented out. This enables them and retires that file.Independent of the Ruby 3.4 PR (#917). Neither touches a file the other does, so they can merge in either order.
The application diff is 8 lines plus a spec. Everything else is the deleted defaults file.
Commits
Load the Rails 7.2 framework defaultsOnly inspect :id on Active Record objects in productionThree things worth a reviewer's attention
1. Four of the five 7.2 options do nothing here
Dumped the settings before and after rather than reading the release notes.
load_defaults "7.2"sets exactly five things:yjitis inert until Ruby 3.4. The initializer is guarded bydefined?(RubyVM::YJIT.enable), andenablearrived in Ruby 3.3; on 3.2.3 the constant exists but the method does not.config/application.rb— sorespond_to?(:active_storage)is false and Rails skips that line entirely.postgresql_adapter_decode_datesonly affects queries carrying no type information. The one raw query in the app,Food.fts, runs throughfind_by_sql, which casts through the model's attribute types, and selects no date columns in any case.validate_migration_timestampsraises on a migration dated more than a day ahead of now. The newest migration here is from October 2017.2.
enqueue_after_transaction_commitis the one real change, and it has a spec:neverenqueued immediately even inside an open transaction;:defaultasks the adapter. Sidekiq answerstrue— both its own adapter and the one still shipped inactivejobdefine it that way, so load order does not matter — meaning a job enqueued inside a transaction now waits for the commit, and a rollback drops it rather than leaving Sidekiq holding a job whose rows were never written.No existing call site changes behaviour. Active Job is reached from exactly three places:
TrackableUsage'safter_commit, which by definition runs with no transaction open;DataExportSchedulesController#create; and sixdeliver_latercalls, all inside Sidekiq workers or rake tasks. None sits in a transaction. Everything else inapp/jobsisperform_asyncon a plain Sidekiq worker, which never enters Active Job.So this is protection for code not yet written, which is why it comes with
spec/jobs/enqueue_after_transaction_commit_spec.rb. Checked in both directions: onload_defaults 7.1two of the three examples fail.The suite is unaffected for a reason worth recording: DatabaseCleaner's
:transactionstrategy opens its wrapping transaction withjoinable: false, andActiveRecord.after_all_transactions_commitskips non-joinable transactions, so a top-levelperform_laterin a spec still enqueues immediately. Had that wrapper been joinable, every job spec would have been waiting on a commit that never comes.3.
attributes_for_inspectdoes not apply toUser#907 deferred
config.active_record.attributes_for_inspecthere on the grounds that it changes behaviour, so it belongs with this flip.load_defaultsdoes not set it; Rails puts it in the generatedproduction.rb.It is worth taking.
inspecton an Active Record object renders every column, and that string is how attribute values reach logs and Bugsnag reports — on this app those values are health data. With[:id],Profile.new(birth_date: ...).inspectis#<Profile id: nil>instead of a line carrying a date of birth, a sex, a time zone and a screen name.full_inspectstill prints everything on demand.The exception, checked rather than assumed:
Useris unaffected, becauseDevise::Models::Authenticatableoverridesinspectand wins the method lookup. Devise's version filters throughserializable_hash, so credentials were never in that output, but the remaining columns still are. Every other Active Record model takes the new behaviour. Development and test keep the:alldefault.This needs no special rollout, contrary to the plan
The upgrade plan flagged this step as a behaviour change needing its own deploy, because some framework defaults change the on-disk format of cached values and signed messages. Checked: none of the five options touches a format.
active_support.cache_format_versionis 7.1 before and after — 7.2 does not change its default, andconfig/application.rbpins it explicitly anyway — so there is no old-process/new-process incompatibility during a rollout.Verification
masterplus the three new ones).standardrb,erblint --lint-allandzeitwerk:checkclean.ActiveRecord::Base.attributes_for_inspect == [:id],Profile#inspectis redacted andProfile#full_inspectis complete.Not verified
docker-compose.ymland therspecjob pin. Nothing here touches SQL or query construction, but CI on the real pins is the gate.yjitcannot be exercised on Ruby 3.2.3 at all, by design — see above.🤖 Generated with Claude Code