Last month I changed jobs and returned to Rails development after a long time.
With a 6-year gap and only one month available for learning, I needed to efficiently grasp Rails 8’s new features and refresh my memory of existing ones.
So I decided to learn by building a working app together with Claude Code while referencing the Rails Guides. I temporarily subscribed to Claude Code’s Max plan at USD 100/mo.
Here’s the repository I created:
Learning Process
I used the Rails Guides as my main reference material.
1. Reviewing the Basics with Rails Guides
Following Getting Started with Rails
, I created /products. I mainly learned about Active Storage (image uploads) and Action Text (rich text).
2. Learning Hotwire and Solid Trifecta
To learn Hotwire and Solid Trifecta, I created /chat and /posts.
/chat: Learned Action Cable, Solid Cable, and Stimulus/posts: Learned Turbo Streams/Frames, Active Job, and Solid Queue. Added comment, follow, and notification features to a typical blog functionality
I learned through AI pair programming with Claude Code. The specific workflow was as follows:
- Create a GitHub Issue (e.g., #14 Follow Feature )
- Have Claude Code create a plan document (e.g., docs/plan-for-issue-14.md )
- Have it create a PR based on the plan while understanding the implementation (e.g., #38 )
I also utilized AI reviews from CodeRabbit. It pointed out N+1 queries and suggested adding tests, allowing me to maintain quality even in solo development. It’s great that it’s free for OSS.
Rails Features I Learned
Hotwire
Hotwire is a framework introduced in Rails 7 for achieving SPA-like UX. Hotwire consists of Turbo and Stimulus.
Turbo is further composed of three parts:
- Turbo Drive: Speeds up page transitions (fetches and replaces HTML on link clicks)
- Turbo Frames: Updates parts of a page (inline editing of comments, etc.)
- Turbo Streams: Updates multiple locations simultaneously (updates list and count when adding comments)
Stimulus is a JavaScript framework, but it’s a very thin layer for adding minimal JS with HTML as the starting point. I used it for UI control in the chat feature.
I utilized Turbo Streams in the comment feature. When a comment is posted, model callbacks broadcast it to other users’ browsers in real-time.
# app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :post, counter_cache: true
belongs_to :user
after_create_commit do
# Append to comment list
broadcast_append_to post,
target: "comments",
partial: "comments/comment",
locals: { comment: self, allow_actions: false }
# Update comment count
broadcast_replace_to post,
target: "comment_count_#{post.id}",
partial: "posts/comment_count",
locals: { post: post }
end
end
Solid Trifecta
“Solid Trifecta” introduced in Rails 8 eliminates the need for external middleware like Redis or Memcached.
- Solid Queue: Database-based Active Job backend
- Solid Cable: Database-based Action Cable adapter
- Solid Cache: Database-based cache store (not used this time)
I used Active Job + Solid Queue for the scheduled post feature. When you set a future datetime for published_at, a job executes at that time to publish the article.
# app/jobs/publish_post_job.rb
class PublishPostJob < ApplicationJob
queue_as :default
def perform(post_id, scheduled_at)
post = Post.find_by(id: post_id)
return unless post
# Idempotency: Skip if already published
return if post.published
# Skip if publish datetime was changed
return if post.published_at.blank? || post.published_at.to_i != scheduled_at
post.update!(published: true)
end
end
You can check job execution status with Mission Control Jobs. Access http://localhost:3000/jobs in development to view job history and queue status in a Web UI.
Other Features
- Eager Loading:
includes/preload/eager_loadto prevent N+1 queries - has_secure_password + rate_limit: User authentication and rate limiting
- Counter Cache: Efficient retrieval of comment counts (
belongs_to :post, counter_cache: true) - Foreign key constraint
on_delete: :cascade: Automatically deletes child records when parent is deleted. Used alongside Rails’dependent: :destroyto maintain DB-level integrity - Shallow Routing: Specifying
shallow: truefor nested resources shortens URLs for member actions (show/edit/update/destroy) - Polymorphic Association: Used for notification feature. Manages follow and comment notifications uniformly
- Self-Referential Association: Used for follow feature. Expresses many-to-many relationships with a single
followstable
Conclusion
Despite a 6-year gap, I was able to learn efficiently with Rails Guides and Claude Code.
My mental model was stuck around the time of “temporarily disabling Turbolinks…”, so I was quite impressed by Hotwire’s ability to achieve SPA-like UX with almost no JavaScript. My memory of Rails was that it often served as an API server or adopted React for the frontend, but I now think Hotwire should be considered first.
Solid Queue and Solid Cable are also nice because you can start with just RDS, reducing the number of components.
Note that I didn’t write many tests this time. The goal was to learn Rails features, so I’ll save test re-learning for another time.
I’ll continue working through the open Issues while continuing to learn.
By the way, after mastering the basics with the Rails Guides, I reinforced my learning with a Zenn book about Rails 8 and Hotwire1. The practical content helped me gain a more concrete understanding of development with Hotwire.