惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Securelist
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
V
V2EX
Google DeepMind News
Google DeepMind News
B
Blog RSS Feed
The Register - Security
The Register - Security
Recorded Future
Recorded Future
Y
Y Combinator Blog
小众软件
小众软件
Jina AI
Jina AI
V2EX - 技术
V2EX - 技术
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
宝玉的分享
宝玉的分享
The Hacker News
The Hacker News
C
Cybersecurity and Infrastructure Security Agency CISA
K
Kaspersky official blog
博客园 - 三生石上(FineUI控件)
T
Threatpost
博客园 - 聂微东
Scott Helme
Scott Helme
IT之家
IT之家
N
Netflix TechBlog - Medium
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
MongoDB | Blog
MongoDB | Blog
T
Tor Project blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
A
About on SuperTechFans
酷 壳 – CoolShell
酷 壳 – CoolShell
C
CERT Recently Published Vulnerability Notes
P
Palo Alto Networks Blog
Spread Privacy
Spread Privacy
C
Check Point Blog
L
LINUX DO - 最新话题
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Last Week in AI
Last Week in AI
Attack and Defense Labs
Attack and Defense Labs
T
Tailwind CSS Blog
罗磊的独立博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Webroot Blog
Webroot Blog
Help Net Security
Help Net Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
爱范儿
爱范儿
PCI Perspectives
PCI Perspectives
Security Latest
Security Latest

Codebase Audits & Rescue | Ally Piechowski

How to Be a Good Open Source Maintainer "The Git Commands I Run Before Reading Any Code" "Ruby 3.2 Is EOL: What You Actually Need to Do" "Why Your Engineering Team Is Slow (It's the Codebase, Not the People)" "Migrating from Sprockets to Propshaft: Is It Worth It?" "How to Close a Tab in Vim" "How I Audit a Legacy Rails Codebase in the First Week" "How to Open a New Tab in Vim" "Rails default_scope: Why You Should Never Use It" "Solved: ActionController::ParameterMissing (param is missing or the value is empty)" "Solved: Warning: Using the last argument as keyword parameters is deprecated" "Vim: How to Open Current Opened File in New Tab" "Rails: How to Use Greater Than/Less Than in Active Record where Statements" "What is the best time for stand-up meetings?" "Using Let and Context to Modularize RSpec Tests" "What Is Fed vs Unfed Sourdough Starter?" "My Father, My Mentor, My Teacher" "How to Get Over Burnout" "What's the difference between a Good Developer and a Good Googler?" Codebase Audits & Rescue | Ally Piechowski
"Rails 7.2 to 8.1 Upgrade: What Actually Breaks and How to Fix It"
"Ally Piechowski" · 2026-03-31 · via Codebase Audits & Rescue | Ally Piechowski

Everything that breaks upgrading Rails 7.2 to 8.1, and how to fix it: enum syntax, the Solid trifecta, Propshaft, params.expect, and every silent regression.

Ally Piechowski · · 8 min read
Rails 7.2 to 8.1 Upgrade: What Actually Breaks and How to Fix It

Rails 8 wants you to drop Redis, drop Sprockets, drop Sidekiq, and adopt a full stack of database-backed infrastructure. None of that is mandatory yet. The gap widens with every minor release.

The Rails 7.2 to 8.1 upgrade itself is one of the smoother major version jumps in recent Rails history. Fewer hard removals, fewer gotchas that prevent the app from booting. The ecosystem migration is where teams get stuck.

TL;DR: Breaking: enum syntax, to_time behavior, schema column reordering | New: Solid trifecta, params.expect, auth generator | Strategy: dual boot on main, not a branch


Before You Touch the Gemfile

Upgrade Ruby First

Rails 8.0 requires Ruby 3.2 minimum, and 3.4 is recommended for 8.1.

Rails Version Minimum Ruby Recommended
7.2 3.1 3.2+
8.0 3.2 3.3+
8.1 3.2 3.4+

If you’re going to Ruby 3.4, two things to watch:

csv was removed from Ruby’s standard library. If anything in your app or gems uses CSV parsing, add gem "csv" to your Gemfile explicitly. This surfaces as a confusing LoadError that doesn’t mention the Ruby version change.

it is now the default block parameter in Ruby 3.4 (like _1 but named). Any code using it as a block-local variable name will silently change behavior. RSpec’s it method is fine since it’s a method call, not a variable, but custom helpers or code inside spec blocks that use it as a local variable will bite you.

Fix Your Deprecation Warnings

On your Rails 7.2 app:

# config/environments/test.rb
Rails.application.deprecators.behavior = :raise

Run your full test suite. The common ones you’ll hit:

  • enum defined with keyword arguments → use positional hash syntax
  • ActiveRecord::ConnectionAdapters::ConnectionPool#connection → use #with_connection or #lease_connection
  • config.active_record.commit_transaction_on_non_local_return → removed in 8.0, decide your behavior now
  • ActiveSupport::ProxyObject → gone, use BasicObject directly
  • Passing nil to form_with model: → use an explicit empty object or omit the argument

These were warnings in 7.2. They’re NoMethodError or ArgumentError in 8.0.

Check Gem Compatibility

bundle outdated
bundle exec bundle_report compatibility --rails-version=8.1
bundle exec bundle-audit check --update  # security vulnerabilities

If you’re doing a broader codebase audit alongside the upgrade, here’s how I approach the Gemfile.

Gems that might break:

  • Devise: check their Rails 8.1 compatibility status. Version 5.x is current; older versions have known issues. Watch for status: :unprocessable_entity becoming status: :unprocessable_content.
  • ActiveAdmin: still catching up. Check their Rails 8 compatibility issues before committing.
  • acts-as-taggable-on: version 12.x supports Rails 8.0, but watch for mb_chars deprecation warnings on 8.2. Check their issues page for the latest compatibility status.
  • SimpleCov: older versions throw REXML::ParseException on Rails 8. Update to latest.
  • WebMock: older versions emit Net::HTTPSession is deprecated warnings on Ruby 3.4. Update.

What Breaks in the Rails 7.2 to 8.1 Upgrade

The Enum Syntax Change

This is the one that bites the most codebases. Rails 8 removes the deprecated keyword argument syntax for enums:

# Before (removed in Rails 8):
enum status: { active: 0, archived: 1 }
enum status: { active: 0, archived: 1 }, _prefix: true

# After:
enum :status, { active: 0, archived: 1 }
enum :status, { active: 0, archived: 1 }, prefix: true

The first positional argument is now the enum name. The second is the mapping. Options like _prefix, _suffix, and _default drop their underscores.

If you have a lot of enums, this is the busywork of the upgrade. A find-and-replace gets you 90% of the way:

grep -rn "enum.*:" app/models/

Regexp.timeout Default

Rails 8 sets Regexp.timeout to 1 second by default for ReDoS protection. Most apps won’t hit this, but if you do complex regex matching against user input, test your edge cases; they’ll raise Regexp::TimeoutError instead of hanging.

Removals

  • bin/rake stats: gone, use bin/rails stats
  • Benchmark.ms: deprecated without an official replacement. Ruby’s Benchmark.realtime is the closest alternative
  • Azure storage service for Active Storage: removed
  • Semicolons as query string separators: removed
  • SQLite3 adapter :retries option: deprecated in favor of :timeout

Rails Console Customizations

rails/console/app, rails/console/helpers, and extending the console via Rails::ConsoleMethods are all removed in Rails 8.0. If you have console helpers in an initializer or a custom console setup, they’ll silently stop loading.

Move any console helpers to a plain Ruby module and include it manually, or use the console block in config/environments/development.rb:

console do
  include MyConsoleHelpers
end

to_time Now Always Preserves Timezone

config.active_support.to_time_preserves_timezone is deprecated in Rails 8.1. The legacy false option is removed, and TimeWithZone#to_time now always preserves the receiver’s named timezone:

# Old behavior (to_time_preserves_timezone = false):
time_with_zone.to_time
# => converted to system local time

# New enforced behavior:
time_with_zone.to_time
# => preserves the named timezone

If your app has config.active_support.to_time_preserves_timezone = false, remove it and test any .to_time calls on TimeWithZone objects.

Leading Brackets in Parameter Names

[foo]=bar in a query string used to parse as {"foo" => "bar"}. In Rails 8.1 it parses as {"[foo]" => "bar"}. If any API clients or forms submit parameter names starting with [, they’ll silently break: params just won’t match what the controller expects.


What’s New in Rails 8.1

params.expect

Rails 8 introduces params.expect as the preferred replacement for params.require(:foo).permit(:bar). The old syntax still works (it’s not removed), but new Rails generators use expect, and it’s stricter about parameter structure.

# Before:
params.require(:post).permit(:title, :body, comments: [:message])

# After:
params.expect(post: [:title, :body, comments: [[:message]]])

params.expect validates the shape of the parameters, not just their presence. If someone sends post=123 instead of post[title]=foo, require would raise but expect handles this more consistently.

The double-array on comments isn’t a typo. [:message] means “a hash with a message key.” [[:message]] means “an array of hashes each with a message key.” params.expect requires you to be explicit about which you expect. Get it wrong and you’ll silently permit nothing.

params.expect returns an ActionController::Parameters object, same as permit. It’s designed to replace the full require(...).permit(...) chain, not be chained onto.

For new controllers, use expect. No need to migrate old ones all at once.

Propshaft Is the Default Asset Pipeline

New Rails 8 apps get Propshaft instead of Sprockets. Existing apps keep Sprockets.

If you want to move to Propshaft, I wrote a full migration guide.

If your pipeline is stable, keep it. Add gem "sprockets-rails" to your Gemfile and move on.

The Solid Trifecta

Rails 8’s headline feature is database-backed replacements for Redis:

  • Solid Queue: background jobs, replaces Sidekiq/Resque
  • Solid Cache: fragment caching, replaces Redis/Memcached
  • Solid Cable: Action Cable pub/sub, replaces Redis

In most apps I audit, Redis exists solely to back Sidekiq and the cache store. Removing that dependency simplifies ops without sacrificing anything.

Your Sidekiq and Redis setups still work; none of this is forced.

When to adopt: If your app doesn’t need Redis-level throughput (most don’t), the Solid stack removes an entire infrastructure dependency. Basecamp and HEY run this in production.

When to wait: If you’re processing 100k+ jobs per minute or need sub-millisecond cache reads, stick with Redis. SSDs are fast, RAM is faster.

Migration strategy: Don’t switch everything at once. Start with Solid Queue for non-critical jobs, monitor, then move cache. Cable last, if at all.

The Authentication Generator

bin/rails generate authentication scaffolds session-based auth with password resets for new apps. If you already have Devise, ignore it.

Schema Column Reordering

Active Record now sorts table columns alphabetically in schema.rb. Your first db:migrate after upgrading will produce a massive diff: every table’s columns get reordered.

This is purely cosmetic (it won’t change your database), but it will bloat your upgrade PR’s diff, cause merge conflicts with in-flight branches that touch migrations, and confuse reviewers.

Fix: Commit the schema.rb rewrite as its own isolated commit. Warn your team. In GitHub PR review, mark the schema.rb file as “Viewed” so reviewers can skip it. If you use structure.sql, column order is preserved; this only affects schema.rb.

Active Job Continuations

Long-running jobs can now break into resumable steps, so they pick up where they left off after a deploy instead of restarting from scratch. Additive; doesn’t break existing jobs.

Deprecated Associations

You can now mark associations as deprecated: true with :warn, :raise, or :notify modes. Useful for large codebases migrating off legacy associations.

Deprecations to Watch

Order-dependent finders (.first without .order), String#mb_chars, and the built-in Sidekiq adapter are all deprecated and will be removed in a future version.


Rails 8.1 Upgrade Strategy

Phase 1: Preparation (on Rails 7.2)

  1. Upgrade Ruby to 3.2 minimum; 3.4 recommended.
  2. Set Rails.application.deprecators.behavior = :raise in config/environments/test.rb and fix every warning.
  3. Fix all enum definitions to use the new positional syntax.
  4. Add gem "sprockets-rails" to your Gemfile explicitly if you use Sprockets.
  5. Add gem "csv" to your Gemfile.

Phase 2: Dual Boot

The next_rails gem provides the next? helper for dual booting:

# Gemfile
if next?
  gem "rails", "~> 8.1"
else
  gem "rails", "~> 7.2"
end

Run CI against both. Merge fixes to main continuously. The upgrade happens on main, not on a branch.

Phase 3: The Cutover

  1. Switch to Rails 8.1 as primary.
  2. Keep config.load_defaults "7.2".
  3. Run bin/rails db:schema:dump immediately. This triggers the schema column reordering before any real migrations run, so you can commit it in isolation. Do this before anyone on the team runs a migration.
  4. Deploy. Monitor.

Phase 4: Default Migration

Uncomment defaults from new_framework_defaults_8_1.rb one at a time. Each gets its own PR and deploy.

Phase 5: Infrastructure Migration (Optional, Separate)

Migrate from Redis to Solid Queue/Cache/Cable. Migrate from Sprockets to Propshaft. Don’t mix them with the framework upgrade.


Quick Reference

# Pre-upgrade checks (run on Rails 7.2)
bundle exec bundle_report compatibility --rails-version=8.1
bundle exec bundle-audit check --update
# railsdiff.org: diff framework defaults between 7.2 and 8.1
grep -rn "enum.*:" app/models/        # find old enum syntax
grep -rn "ProxyObject" app/            # removed in 8.0
grep -rn "read_encrypted_secrets" config/  # removed

# Post-upgrade essentials
bundle update rails
bin/rails app:update
bin/rails db:migrate
bin/rails test  # or: bundle exec rspec

# Check for Regexp timeout issues
grep -rEn "Regexp\.(new|compile)" app/ lib/

If that branch has been sitting untouched for months, get in touch. This is what I do.


Related Articles