Hyperstack vs Hotwire

Catmando
6 min readFeb 26, 2021

--

This is a very opinionated, but I hope honest and accurate comparison of Hyperstack vs. Hotwire.

First off what are these things?

Both are ways to integrate a modern UI into rails.

Both are integrated with websockets for push notifications of database changes

Disclaimer — I am one of the contributors to Hyperstack, so like I said this is opinionated. However, Hyperstack is dog food I eat daily and have bet my business on.

Hyperstack

  • Everything is written in Ruby (including client-side code)
  • Uses React under the hood for building the UI side of things and allows access to React component libraries
  • Seamlessly keeps Rails models synchronized between the UI and the server, in many cases with no extra code.
  • Eliminates the need for the Controller layer (but you can still use controllers if you want)
  • Pushes as much work as possible onto the client-side
  • Provides a powerful data access control mechanism built on the “Pundit” policy concept.
  • Can be used with existing Rails sites.

Hotwire

  • Is the next evolution of Rails Turbolinks
  • Augments the traditional MVC Rails system by intelligently pushing out incremental updates to the view layer.
  • Uses traditional Rails concepts such as Controllers, and ERB view files.
  • Keeps a lot of work on the server-side, making the client-side very slim.
  • Eliminates a lot (but not all) of the JS code you might have to write.
  • Has the backing of DHH and presumably the Rails community.

To give a very quick comparison of these technologies, I took a look at the simple “Tweet” app discussed in this podcast (https://gorails.com/episodes/hotwire-rails) and built the same app in Hyperstack. Note: The actual UI design here is not my idea, I just took the code given and translated it with as little change as possible.

Since one of the big goals of Hyperstack is to reduce overall code size let's start with a look at the total lines of code — not including generated boilerplate files such as the layout and styles that are the same.

Hyperstack - 78 lines vs — Hotwire — 156

But lines of code are not everything, which one is going to be easier to understand and maintain?

Let’s look at how each framework implements the “Tweet Card” that displays each tweet and allows for the updating of the “likes” and “retweets” counter.

Hyperstack:

Hotwire:

So far the two piles of code look very similar.

Hyperstack defines a React component that takes a tweet parameter and then lays out the code using a DSL that maps to the underlying HTML tags plus any application-defined components (such as the EditTweet component “mounted” on line 11)

Hotwire uses ERB to generate the HTML with a number of “view helpers” such as turbo_frame_tag etc.

Like React Hyperstack is declarative and state-driven. When the user clicks the edit button, this updates the editing state, causing the component to render and mount the EditTweet component. (Line 14) The EditTweet component will fire the save or cancel events, both of which return the editing state to false. The work is defined and performed on the client.

Hotwire’s goal is to keep the logic on the server, thus when the user clicks Edit, this simply does a post to the server, which will do the work of updating the component.

When the user clicks the “Like” or “Retweet” button on the Hyperstack UI the event is handled by the .on(:click) handler, which simply calls the ActiveRecord increment! method to update the count in the tweet. Under the hood, Hyperstack takes care of keeping the local (UI) copy of the tweet in sync with the server’s copy and rebroadcasting any changes to any other participating browsers.

Hotwire meanwhile handles the counter updates the same way as the edit function: It delegates the work to the server, and so there must be code up there to handle this. Indeed there are three controllers defined that do the real work of dealing with changes to the tweet:

So while the Hyperstack code initially appears slightly longer than the Hotwire code, the UI portion was not the whole story. For the html.erb file to actually work, another 70+ lines have to be added, and what is worse the UI logic is now split between 4 different files.

And we are not done. In addition to the .erb file, and the controllers we also need a json file that acts as a kind of API glue logic. Luckily Hotwire comes with jbuilder, so this file is just another 2 lines that you need to add and maintain.

Hold on, we are not quite done yet. In order for Hotwire to know that it needs to broadcast changes to the client, you have to announce this by adding three more declarations (after_…) to the twitter.rb file.

Are we there yet? Well almost. One more file to go. Remember the controllers? Well, you have to route to them right? That’s 3 more lines to deal with in yet another file. In all fairness, Hyperstack also requires a few lines (2 to be exact) in the routes.rb file, but those two lines never have to change for the life of the application.

But before I move on, let's look at this code another way. Say that you had a working system, to which you wanted to add the like attribute and button.

What would it take?

First in common for both systems you would add the attribute to the model by creating and running a migration (yeah rails!)

Once that is done here is what you would do in a Hotwire system:

  • add the resource to the routes file,
  • add a controller (as we see above about 14 lines of code,)
  • don’t forget to update the json file and add the new attribute there,
  • and finally, we add our button_to tag in the _tweet.html.erb file.

We have touched 4 files, added 16 lines of code, used a working knowledge of 6 different Rails subsystems — action controller, view helpers, jbuilder, active record, and the router — and wrote our code in a mix of Ruby, html, and ERB.

On the other hand the same task in Hyperstack?

Once the migration is complete just add the following line:

BUTTON { "Likes (#{tweet.likes_count})" }
.on(:click) { tweet.increment!(:likes_count) }

For which you have to understand active record for the increment! method and the HyperComponent DSL, both of which are directly related to the task at hand.

What is the catch?

There is no catch, it's just a different set of goals. Hyperstack was built to maximize programmer productivity and to offload work from the central server down to the client. It achieves those goals by using a single wonderful language throughout, building on Rails, and using the Rails motto of convention over configuration to eliminate needless boilerplate.

Hotwire’s stated purpose is to keep control in the server, and it achieves that goal, but at quite an expense to the maintainability and understandability of the software. It also takes a more conservative approach in its design, rather than throw out the need for controllers, and replacing ERB files with a Ruby DSL, it builds on tried and true Rails techniques, which many people may be comfortable with.

Some Other Considerations

This is a very limited exercise, but having looked at how the two systems accomplish things, I believe things just get harder (sorry) with Hotwire, while they get easier with Hyperstack. I can’t prove that, but like I said this is an opinionated piece.

Another consideration is the value of participating in the React ecosystem. The fundamental architecture of React, as a declarative state-driven system, makes the resulting code incredibly simple to write and understand. I believe it is far easier (once you have gotten through the basic learning curve of course) to produce highly functional, and maintainable code in this declarative style. Furthermore, there is a wealth of ready-made React components to solve every conceivable problem, further reducing the amount of code you have to write and maintain.

A final consideration is the value of developing in a single language. Many people do not believe this is a big factor, and that dealing with several languages and systems is just the programmer's job, and is no big deal. Neither is true. Constantly context switching causes the programmer to waste valuable energy, and to focus on implementation details, rather than thinking about the system as a whole. There is nothing that makes one language better at UI development and another language better for the server-side. We have the tools to compile just about any language to any platform, so why not pick a single language that can be used system-wide? And if you are going to pick a language then it is hard to beat Ruby.

More Info on Hyperstack

A comparison of Hyperstack and Vue in 2 parts:

--

--

Catmando

AKA Mitch VanDuyn, is the founder and chief mad scientist at CatPrint.com, an internet printing company.