RVTWS
A Ruby stack for modern web apps
June 24, 2022 ¡ Felipe Vogel ¡When I started my career switch into software development two years ago, I decided to focus my efforts on Ruby. I did this for a few reasons, but one of them is that Rails offers great âbang for the buckâ: thereâs a lot that I can build with just Rails, HTML, and CSS.
However, this minimal vanilla stack becomes limiting when two factors come into play:
- The MVC architecture of Rails wonât always be enough to keep your code organized. Youâll notice it painfully if your app ever grows beyond a small project.
- For your app to feel modern, the front end will need to act like an SPA (Single-Page Application). The âofficialâ way to do this is now Hotwire, but there are other tools worth keeping in the toolbelt. More on that below.
Making a new acronym for your favorite tech stack is a popular thing these days, so Iâll coin a new acronym: RVTWS⌠pronounced âerv toesâ? Yes, this is great. It will go viral in no time.
Joking aside, Iâm using this acronym here only as an outline for this blog post, rather than for any marketing value. The âVâ (ViewComponent) addresses point #1 above, and âTWSâ are concerned with #2.
- Rails or Roda
- ViewComponent: for front-end architecture
- Turbo: for an SPA feel, using the server
- Web Components: for an SPA feel, using the client
- StimulusReflex: for more complex front-end magic
Rails or Roda
Thereâs not much to say about Rails: itâs boring tech, and therefore a good choice for most web apps.
Roda is also worth considering. Unlike the batteries-included philosophy of Rails, Roda is bare-bones by default but highly extensible. This makes Roda apps fast and more architecturally flexible. Besides the docs, here are some places to get started with Roda:
- An interview with its creator Jeremy Evans.
- Roda + Sequel app skeleton, which has a similar purpose to the
rails new
command. - A post on Bridgetownâs ongoing Roda integration, which is making Roda more accessible thanks to Bridgetownâs batteries-included approach.
ViewComponent
Partials are the standard Rails way to define distinct parts of a view. Partials provide a way to separate out part of a template, but they donât provide a way to separate out view-related logic, which often ends up being thrown into models and/or controllers. This is one reason why models and controllers in Rails can so easily become huge and messy.
ViewComponent provides a home for this view-related logic. This post on the Code with Jason blog explains it best. In short, ViewComponent fills a big gap in MVC architecture.
Some readers may be wondering, âIs that all? What about other architectural improvements, like service objects?â Itâs true that certain types of apps have problems best solved with certain design patterns, and maybe a very large app needs a specialized architecture. But in general, Rails models designed in a careful, object-oriented way can take you very far, and I think the popularity of service objects is unjustified because they can easily muddle up a codebase. Food for thought:
- Why Service Objects are an Anti-Pattern at Fullstack Ruby
- How I Organize My Rails Apps at Code with Jason
Turbo
On to the front end! Turbo is part of Hotwire, which now ships with Rails. Turbo makes it really easy to give server-rendered pages a snappy SPA feel, where parts of the page are updated instantly instead of a full page reload.
At the heart of Turbo is âHTML over the wireâ (for which HOTWire is an acronym), which means the server sending HTML fragments for partial page updates, which (hereâs the big win) eliminates the need for client-side state management. There are lots of tools taking this approach now.
Unpoly and HTMX are two of the most intriguing alternatives to Turbo because they are more framework-agnostic and have a flexible, concise syntax. Turbo, on the other hand, seems easier to get started with if youâre in Rubyland.
Sidenote: If youâre wondering why all these âHTML over the wireâ tools came about and what theyâre pushing back against, take a look at these two comparisons of web app architecture from 2005 vs. ten years later: one from Unpoly (2005) vs. 2015), and another from the developer of Turboâs predecessor Turbolinks (2005 vs. 2016).
Besides Turbo, the other part of Hotwire is Stimulus, which typically is used for adding client-side reactivity in situations where you want to sprinkle in some JS. After all, you wouldnât want every user interaction to involve a round trip to the server. So why am I including only Turbo here and not Stimulus?
Web components
Actually, Stimulus is pretty cool because you can compose multiple pre-built behaviors into one Stimulus controller, for a sort of functional approach to component behaviors. The tradeoff is that a growing web of Stimulus controllers (plus HTML data attributes associated with them) can become complex and hard to understand.
Web components are an architecturally simpler way to add client-side behaviors, and they also have the advantage that theyâre a web standard. This blog post on Fullstack Ruby shows the power of web components in the context of Ruby.
Also, as illustrated in that post, you can use Ruby2JS to write web components in Ruby. (You can likewise write Stimulus controllers in Ruby.) In other words, you get the best of both worlds: the power of JavaScript on the front end, and all the conveniences of Ruby syntax đ¤Š
StimulusReflex
Turbo + web components can take you a long way in making your Rails app feel modern, but there are other tools in this space that can complement them. Iâve already mentioned Stimulus, but thereâs also StimulusReflex which is like Stimulus but on the server, and CableReady which is somewhat like Turbo Streams but more flexible. Be sure to give these a try if your app is highly interactive, or if you just want to expand your horizons beyond Hotwire.
Also, the StimulusReflex Discord server is an amazing place even for discussing all the other tech mentioned in this post. (Which is very ironic. Ever since Hotwireâs release, it has marketed itself as THE solution, not even mentioning the similar tools had already been coming out of the StimulusReflex community for two years. Even so, itâs in this very community that you can find the best Hotwire support.)
Conclusion
Iâve been thinking about this ideal Ruby stack because for the first time Iâm working on a Rails app that has a React front end, and itâs been a painful adjustment. Sometimes it feels like Iâm writing logic twice, once on the back end and again on the front end. And what do I get for it? Smoother page transitions and buttons that do things without refreshing the page. Thatâs nice, but do these simple enhancements have to involve so much extra work?
On the other extreme, the âvanilla Railsâ approach to writing views is clunky and outdated, and Hotwire doesnât fix all the omissions. Itâs no wonder that people go looking for outside front-end solutions such as React.
So I started dreaming about what front-end tools could give a smooth user experience without so much extra complexity, and this âRVTWSâ stack is the result. (Yeah, I need to work on that acronym.)
On a final note, if youâd like to learn more about Hotwire, check out the Hotwire resources that Iâve compiled in my âLearn Rubyâ list. UPDATE: That link is to an old version of the list. The current version omits Hotwire because these days Iâm more interested in other JS libraries. See the bottom of the âJavaScriptâ section in the list.