The methods update_columns and update_column are well-known for their speed because they bypass all Rails validations and callbacks. However, they had a major “side effect”: by default, they wouldn’t update the updated_at timestamp.
PR #51455, merged in July 2025, solves this dilemma by allowing you to update specific columns and the timestamp in one go. Even though this was introduced a few months ago, it’s a game-changer worth remembering for any performance-tuned Rails app.
The Problem: Performance vs. Timestamps
Whenever we used #update_columns to gain performance (avoiding expensive callbacks), we lost the information of when the record was actually modified—unless we manually updated the updated_at field:
1
2
3
4
5
6
7
# Before: You had to update the timestamp manually
user.update_columns(last_login_at: Time.current, updated_at: Time.current)
# Or run two separate database commands
user.update_column(:last_login_at, Time.current)
user.touch
This led to repetitive code and was prone to bugs, especially for ETL processes or cache keys that rely heavily on the updated_at field.
The Solution: The new :touch option
Now, you can simply pass touch: true to ensure that Rails’ audit timestamps are updated within the same SQL query.
Practical Example:
1
2
3
4
5
6
# Updates the column and updated_at in a single, efficient query
user.update_columns(active: true, touch: true)
# It also works for a single column update
user.update_column(:active, true, touch: true)
You can even pass a specific time if needed:
user.update_columns(active: true, touch: { time: 1.day.ago })
Why It Matters
- Performance with Integrity: You keep the speed of bypassing callbacks without “breaking” the application’s timestamp logic.
- Single Query: Reduces database round-trips, as you no longer need a separate
touchcommand. - Clean Code: Removes the need to explicitly include
updated_at: Time.currentin every direct update call.