Skip to main content
Test Double company logo
Services
Services Overview
Holistic software investment consulting
Software Delivery
Accelerate quality software development
Product Management
Launch modern product orgs
Legacy Modernization
Renovate legacy software systems
DevOps
Scale infrastructure smoothly
Upgrade Rails
Update Rails versions seamlessly
Technical Recruitment
Build tech & product teams
Technical Assessments
Uncover root causes & improvements
Case Studies
Solutions
Accelerate Quality Software
Software Delivery, DevOps, & Product Delivery
Maximize Software Investments
Product Performance, Product Scaling, & Technical Assessments
Future-Proof Innovative Software
Legacy Modernization, Product Transformation, Upgrade Rails, Technical Recruitment
About
About
What's a test double?
Approach
Meeting you where you are
Founder's Story
The origin of our mission
Culture
Culture & Careers
Double Agents decoded
Great Causes
Great code for great causes
EDI
Equity, diversity & inclusion
Insights
All Insights
Hot takes and tips for all things software
Leadership
Bold opinions and insights for tech leaders
Developer
Essential coding tutorials and tools
Product Manager
Practical advice for real-world challenges
Say Hello
Test Double logo
Menu
Services
BackGrid of dots icon
Services Overview
Holistic software investment consulting
Software Delivery
Accelerate quality software development
Product Management
Launch modern product orgs
Legacy Modernization
Renovate legacy software systems
Cycle icon
DevOps
Scale infrastructure smoothly
Upgrade Rails
Update Rails versions seamlessly
Technical Recruitment
Build tech & product teams
Technical Assessments
Uncover root causes & improvements
Case Studies
Solutions
Solutions
Accelerate Quality Software
Software Delivery, DevOps, & Product Delivery
Maximize Software Investments
Product Performance, Product Scaling, & Technical Assessments
Future-Proof Innovative Software
Legacy Modernization, Product Transformation, Upgrade Rails, Technical Recruitment
About
About
About
What's a test double?
Approach
Meeting you where you are
Founder's Story
The origin of our mission
Culture
Culture
Culture & Careers
Double Agents decoded
Great Causes
Great code for great causes
EDI
Equity, diversity & inclusion
Insights
Insights
All Insights
Hot takes and tips for all things software
Leadership
Bold opinions and insights for tech leaders
Developer
Essential coding tutorials and tools
Product Manager
Practical advice for real-world challenges
Say hello
Developers
Developers
Developers
Software tooling & tips

Work around Rails schema noise with one weird trick

Discover a simple and effective trick to handle unwanted changes in db/schema.rb on your Rails projects. Perfect for lazy developers!
Kevin Baribeau
|
April 8, 2024
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

If you’ve ever worked on a Rails project with a team, you’ve probably run into an issue with changes appearing in db/schema.rb. The Rails robots that make up Active Record do their best to be helpful by keeping your db/schema.rb file up to date. But while they’re doing that, they tend to inject a bunch of other unwanted changes.

Here’s an example from a project I’ve been working on:

@@ -102,7 +102,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_02_06_230921) do

   create_table "active_storage_attachments", force: :cascade do |t|
     t.bigint "blob_id", null: false
-    t.datetime "created_at", null: false
+    t.datetime "created_at", precision: nil, null: false

In this project, db/schema.rb has almost 4000 lines, and over 800 get automatically changed any time I run a migration. It’s a little annoying.

Not every change is exactly like the above example with precision. There are a few other small issues, but they all have the same root cause and fix.

It’s me, hi, I’m the root cause, it’s me.

The problem is that I’m lazy.

I know that the changes are because I’ve let my local database fall out of sync with what my local versions of Rails and Postgres work together to generate. In the case of these “precision” changes, it’s because the project has been upgraded to Rails 7 sometime after I created my database.

What can we do about it?

I could update my local database to include these precision constraints. But as you might guess, that comes with a few problems:

  • I’d have to find every column that needs updating, and make that change
  • Even if I did that, I’d run into other changes either caused by the Rails upgrade, or some other production change that didn’t affect my local database
  • As mentioned above: I’m lazy

I could also completely drop my local database, and re-create it from scratch. Our team even has scripts to help with this. But I’m far lazier than the scripts are helpful. I just can’t be bothered.

Honestly my favrourite solution here is to ignore the problem: I’ll git checkout db/schema.rb and move on with my life.

But sometimes that’s just not good enough. Just the other day, I needed to create a new migration.

My lazy self often wants to use git add -p to select which changes I need, and continue to ignore the reset. But as I said earlier, I’ve got over 800 changes to sift through on this project. That’s too many for me to want to click through. The / search option on git add -p could help…but thankfully, laziness is a super-power; it led me to a better idea.

So what’s my weird trick? It’s just a plain-old git commit.

Step 1: Commit everything that we don’t want

Before adding my migration, I knew I’d run into this problem. What I wanted to do was capture the entire mess. So I triggered it by rolling back and re-running the most recent migration:

> git checkout main
> bin/rails db:rollback
> bin/rails db:migrate

Now, I have the complete set of changes in db/schema.rb that I don’t want to have to deal with. I can commit them:

git add db/schema.rb
git commit -m “Unwanted database changes”

Step 2: Commit the things we do want

At this point, I was feeling pretty good. I knew I could just pretend everything was fine, and my local database isn’t all kinds of messed up. It would all be OK.

I went ahead and did the plain-boring-old method for adding a migration:

> bin/rails generate migration AddPartNumberToWidgets part_number:string
      invoke  active_record
      create    db/migrate/20240320191332_add_part_number_to_widgets.rb

> bin/rails db:migrate
== 20240320191332 AddPartNumberToWidgets: migrating ===========================
-- add_column(:widgets, :part_number, :string)
== 20240320191332 AddPartNumberToWidgets: migrated (0.0058s)


> git commit -m "Add part_number to widgets"
[main 3b865550c] Add part_number to widgets
 2 file changed, 6 insertions(+)
 create mode 100644 db/migrate/20240320191332_add_part_number_to_widgets.rb

Step 3: Take out the trash, but keep the good stuff

Remember that first commit? We don’t need it. Let’s throw it out like the trash it is. The simplest/quickest way to do that was to:

> git reset --hard origin/main .

And now finally, I’d gotten to the magic bit. I had removed both commits, but the one I wanted was easy to bring back. All that’s needed is the SHA, which git commit had helpfully outputted earlier. With that, I could bring it back with a cherry-pick:

> git cherry-pick 3b865550c

And that’s it!

Now we have a single commit containing only our new changes, without any noise. It’s beautiful AND easy.

Related Insights

🔗
How test_data speeds up Rails tests
🔗
Why it’s worth outsourcing your Rails upgrades
🔗
14 tools every Ruby developer will love for performance and debugging

Explore our insights

See all insights
Developers
Developers
Developers
You’re holding it wrong! The double loop model for agentic coding

Joé Dupuis has noticed an influx of videos and blog posts about the "correct" way of working with AI agents. Joé thinks most of it is bad advice, and has a better approach he wants to show you.

by
Joé Dupuis
Leadership
Leadership
Leadership
Don't play it safe: Improve your continuous discovery process to reduce risk

We often front-load discovery to feel confident before building—but that’s not real agility. This post explores how continuous learning reduces risk better than perfect plans ever could.

by
Doc Norton
Leadership
Leadership
Leadership
How an early-stage startup engineering team improved the bottom line fast

A fast-growing startup was burning cash faster than it could scale. Here’s how smart engineering decisions helped them improve the bottom line.

by
Jonathon Baugh
Letter art spelling out NEAT

Join the conversation

Technology is a means to an end: answers to very human questions. That’s why we created a community for developers and product managers.

Explore the community
Test Double Executive Leadership Team

Learn about our team

Like what we have to say about building great software and great teams?

Get to know us
Test Double company logo
Improving the way the world builds software.
What we do
Services OverviewSoftware DeliveryProduct ManagementLegacy ModernizationDevOpsUpgrade RailsTechnical RecruitmentTechnical Assessments
Who WE ARE
About UsCulture & CareersGreat CausesEDIOur TeamContact UsNews & AwardsN.E.A.T.
Resources
Case StudiesAll InsightsLeadership InsightsDeveloper InsightsProduct InsightsPairing & Office Hours
NEWSLETTER
Sign up hear about our latest innovations.
Your email has been added!
Oops! Something went wrong while submitting the form.
Standard Ruby badge
614.349.4279hello@testdouble.com
Privacy Policy
© 2020 Test Double. All Rights Reserved.