One of the biggest lessons I learned in scaling Rails apps is this: not every request needs to hit the database. When traffic grows, even well-optimized SQL queries can slow things down. That’s where caching comes in — and Redis became my go-to tool.
Reduces database load
Improves response time (milliseconds instead of seconds)
Handles spikes in traffic gracefully
Used to serve entire pages as static HTML. Mostly replaced by reverse proxies like NGINX or Cloudflare.
Rails lets you cache parts of the view:
<% cache(@post) do %>
<%= @post.title %>
<%= @post.body %>
<% end %>
If @post doesn’t change, Rails serves this fragment from cache.
For more control, we use Rails.cache :
# Store data
Rails.cache.write("user_#{user.id}_posts", user.posts, expires_in: 10.minutes)
# Read data
posts = Rails.cache.fetch("user_#{user.id}_posts") do
user.posts.to_a
end
Here, Redis stores the posts in memory. If cache is empty, it falls back to DB and repopulates.
We had an app pulling exchange rates from an external API. Without caching, it made an API request every time. With Redis caching:
def exchange_rates
Rails.cache.fetch("exchange_rates", expires_in: 1.hour) do
ExternalApi.get_rates
end
end
Now we only hit the API once per hour, saving cost and latency.
The hardest part of caching is knowing when to expire data. Strategies I’ve used:
Time-based expiry (expires_in: 10.minutes)
Manual busting when a record updates:
after_save :clear_cache
def clear_cache
Rails.cache.delete("user_#{id}_posts")
end
Always watch cache hit rate. If most requests miss cache, you’re wasting Redis memory.
Caching with Redis turned some of my apps from “slow under load” to “snappy and scalable.”
The big lesson: caching isn’t just about speed — it’s about designing your system to handle growth without burning the database.