Ok now look. This is an article about the dynamics of the Ruby web ecosystem. I’m going to explore the status quo, what it would take to change the status quo, and why that matters. I’m going to make a prediction: that in 5 years time, while Rails may not be the dominant framework, Hanami will definitely not be. And before we go any further, I want to get two thing straight:
First: This article is not about criticising the quality of Hanami’s code. I’m well aware that Hanami is in v1.0.0, and that, being so young, its code will of course be flawed, and that that’s not indicative of anything per se. It’s also not just a dig at Hanami. I like Hanami. I am going to be critical of Hanami, but the criticisms aren’t about dismissing Hanami as simply ‘bad’, they’re about assessing Hanami’s place and promise in the ecosystem as a whole.
Second: I am well aware that I am making quite a bold assertion in the title of this article. I am well aware that this is going to be quite a short article, and therefore that by the end of it you may not feel like I’ve fully defended the assertion. That’s fine. I’ll do my best to defend it, and I’ll happily listen to counter-arguments pointing out the flaws. But I do genuinely believe what I’ve written in the title, and what I hope is that this article will show you why I believe that, even if you disagree with me.
The State of Play
So let’s get started. And let’s start with the status quo, which is: Rails. Rails basically is the Ruby web ecosystem. Sure, some people use Sinatra, and a few use Ramaze, but the numbers are totally dwarfed by the Rails behemoth. It has over 3,000 contributors compared to Sinatra’s 300-odd, and there are more questions on Stack Overflow about Rails than there are about Ruby.
Now this isn’t particularly surprising, when you think about where Rails came from. Rails wasn’t just a framework, it represented a philosophical movement, and it wasn’t positioning itself as an alternative to the other Ruby frameworks that were around at the time. It was positioning itself as an alternative to Java and PHP, and the people who adopted it weren’t just Rubyists, they were web developers fleeing from all the sins of a multitude of languages.
And the thing about dominance in this kind of sphere is that it’s self-reinforcing. Rails is better supported by the community than all the alternatives, because it has more community knowledge online, more contributors making the codebase better, faster, and more third party libraries write explicit integrations with Rails than other frameworks. This encourages people to use Rails by default, and that use just leads to more community knowledge, contributions and integrations.
But Rails has a problem, which is that for all that it’s used a lot, it’s also disliked a lot. Some people loudly and publicly loathe it, some people quietly grumble about it, but whatever the volume individually, collectively it adds up to a lot of negativity. Where does that come from? Allow me a brief detour and I’ll explain.
The 3-Act Tragedy of Rails
Experienced coders who come to Rails, particularly those who come from a non-Ruby background, tend to go on a three-stage journey.
The first stage is the ‘Where’s all the code?’ stage. This is where you find there’s something unsettling about how automatic everything is. For all that Rails is about ‘Convention over Configuration’, when you start building your first Rails app and you realise that you can get half the functionality you want by using scaffold generators from the command line, and another quarter by installing a handful of gems like Devise, it feels a little bit like configuration is all there is to do. There doesn’t seem to be much opportunity to write any actual code. If you’re used to working with ‘Not Invented Here’ languages like .NET, this can make the whole thing feel a bit joyless.
But then comes stage two, which is the infatuation stage. This happens when you get it, and it normally occurs when you go back and build your second app with Rails. By now you’ve got the hang of the Rails-y workflow, and you find yourself marvelling at just how blazing fast it is to get up and running. Migrations, callbacks, validations, forms helpers, everything is at your fingertips to make massive changes quickly. In this stage, in many respects, you’re at your most dangerous. You believe that Rails magic can accomplish anything, and you find yourself building huge towering edifices of code, with everything sitting in the top level of the ‘models’, ‘views’, ‘controllers’, and ‘helpers’ folders. The codebase gets pretty smelly, pretty quickly, but that flush of mad, passionate love for the framework stops you seeing the abomination it is helping your code to become.
The final stage really ought to be a Hegelian synthesis, a Goldilocks-like middle ground between the initial suspicion and the subsequent fanaticism. But actually the final stage is grudgy resentment. This is where you realise that Rails is particularly well optimised towards building something small quickly, and that the building blocks of a Rails application scale really, really badly as a codebase grows. You’ve had to manage at least one vast and poorly-structured monolith (probably one you built yourself), and have seen just how much trouble too much magic can get you into if you don’t know what you’re doing. You find yourself longing for POROs, hating the complexities of ActiveRecord callbacks when dealing with subclasses of models, cursing the way instance variables you set on a base controller somewhere end up being referenced inside view templates, and wishing someone would come along and make something that offered the speed of Rails development without the bloaty mess that so often follows.
Ready for a Change
So at this point you might be thinking that not only would change be possible, it might be a good thing too. Rails served a magnificent purpose, but perhaps the time has come to find a new framework that doesn’t leave the bitter taste in the mouth that prolonged Rails exposure leads to.
Now, here’s the first really controversial point I’m going to make: I don’t believe that simply being better than Rails is enough to displace Rails. If Rails was simply the best of the bunch, then a framework that was better than Rails would do better. But there is no bunch. There is Rails, and there is a group of stragglers eating the crumbs that Rails drops on its merry way. There’s far too much inertia in the status quo. Simply being better won’t cut it, because each new developer doesn’t compare the market and pick the best framework. They do what they’re told, and what they’re told is Rails.
So what will make a difference? Well, if you can’t capture new developers to the scene, you have to convert existing developers. And I believe that you’ll only attract Rails-using developers away from Rails if your framework specifically avoids or fixes the problems people have with Rails. Let me repeat this: It’s not about being better than Rails. It’s about marketing yourself as the solution to the problems of Rails.
Furthermore, if you really want to beat inertia, you have to exert an equally powerful force: Love. If anything becomes the next Rails, it will need to be something that causes developers to fall in love with it, just like the Rails infatuation stage described above.
Ok, I think we’re finally ready to talk about Hanami.
A New Challenger
So, Hanami appears, and everyone gets very excited. It’s more full-featured than Sinatra, less specialised than Grape, and above all, it advertises itself as being cleaner than Rails. Over the course of 3 years it’s gone from v0.1.0 to v1.0.0, and in that time quite a few people have made noises about whether it’s the future.
There’s no denying it. Hanami is nice. It’s clean. It positions itself as the lightweight-but-full-featured alternative to Rails, where there’s no magic, because “it’s just Ruby”.
From a standing start, I think Hanami is probably better than Rails. On the one hand, it has that quick and easy feel that makes Rails users fall in love. On the other, things are much neater. For example, it’s almost impossible to manually instantiate a controller in Rails, because you need to do so much setup. Whereas in Hanami, you just… instantiate it. Simple as.
As I mentioned above, I don’t think you can unseat Rails without explicitly solving the problem that Rails has, and the problem Rails has is magic. Hanami undoubtedly has less magic. But it doesn’t have no magic. And I think that’s ultimately why in 5 years time Hanami, even if it’s used and loved by thousands, won’t be the top dog in the ecosystem. Because it doesn’t have what it takes to trigger a mass migration. Let’s look at an example.
Prepend: A Case In Point
If you look at the Hanami getting started guides, you’ll see a spec for a controller action that looks like so:
it "is successful" do response = action.call(params) response.must_equal 200 end
Oh lovely. The return value of the action’s call method is, as the guide states, a “Rack-compatible array of status, headers and content”.
But hang on. In the very next chunk of code in the guide we are shown an implementation of the controller action to make the spec pass. And it looks like this:
def call(params) @books = BookRepository.new.all end
Huh? I thought
call had to return an status/header/content array. What’s going on there? Well, a quick dive into the /source shows us this:
module Action # ... snip ... def self.included(base) base.class_eval do # ... snip ... prepend Callable end end # ...
Oh look. We’ve prepended
Callable, which has its own
call method that looks like this:
def call(env) _rescue do # ... snip ... super @params end finish end
This means that any time you include the
Web::Action module in a class, and write a
call method, what that method returns will not be what is returned by what you wrote. That, to me, is not very nice.
Now look, I’m not saying it’s never a good idea to use
prepend. (It’s what I’m thinking, sure, but I won’t go so far as to say it.) But what I will say is that it introduces a ton of complexity. It’s much harder to reason about what’s happening when you’re prepending left right and centre. But my personal opinions aside, what
prepend is, really, is magic. And more than that, in this case it’s pretty avoidable magic. It would have been perfectly possible to ask that the user-written method inside the action class have a different name – ideally a name that represents what the purpose of that method is inside the broader
call process – and then have the
call method invoke that other method. No
super, no re-use of the same method name to refer to two separate methods.
I think I get why they did it. You write a
call method in your action, and you test the
call method in your tests. Simple. Intuitive. Magical. And for the most part, it works. But it does get messy when you start trying to debug when something goes wrong. Or if you want to do something crazy like, say, subclass your action and override
call, and call
super inside your subclass, and it doesn’t work, and you can’t figure out why. This is the kind of thing that experienced Rails developers dislike.
So why does Hanami have little bits of magic in it? Well, the primary answer is that Hanami isn’t trying to unseat Rails. They’re trying to create the best framework for Rubyists, and aren’t bothered about whether everyone and their dog adopts them. In their opinion, a little bit of magic, applied in the right place, is fine. The Hanami approach, as described by its creator, is all about pragmatism. In this case, perhaps later down the line, someone might come up with an argument why that particular use of
prepend is unnecessary, and, when weighing up the pros and cons, it might be decided that they’re right, and things might be changed. But no one’s ditching stuff like this on any ideological principle.
And I think that’s what it really comes down to. The thing about Rails is that its founder is an idealist, a fanatic. He has inspired hatred from the start, but he has also inspired a generation of developers. What propelled Rails forward was a vision for what coding could be like, and slowly but surely Rails got close to that vision. Hanami doesn’t have a vision, and that’s both its strength and its weakness.
It’s a strength because Hanami can be flexible – it can cherry-pick the best of everything, because it’s ideologically opposed to nothing. Whereas for Rails, the all-consuming vision of what the framework should be requires them to put in, amongst all the goodness, the bad stuff that causes people so much grief in the end.
It’s a weakness because it’s harder for people to fall in love with pragmatism. Even if a pragmatic approach makes Hanami “objectively” the best Ruby framework, without people falling in love with it, they won’t stampede towards it in droves, and so it won’t unseat Rails. The Hanami community may be fine with that, of course. But I can’t help but wish that something would come along that had a shot at being both the best and the most popular.
Ok, that’s it. That’s my argument. I hope it hasn’t come across as simply being: “Hanami sucks because it uses
prepend.” Ultimately I’m saying that the dynamics of the Ruby web ecosystem are driven by ethos, and that Hanami’s ethos, if it even makes sense to say it has one, isn’t of the sort to upset that ecosystem. If you think my argument’s a stretch… well, I’d love to hear your opposing views in the comments!
- This article has been substantially edited after an earlier version got quite a bit of flak for being “bitchy clickbait”. My apologies to anyone who was offended by the initial version. I hope my subsequent updates have done something to improve the tone.