Rails: We need to talk.

An open letter to my framework

Jeff D.
13 min readJul 8, 2014

I remember when we first met. I had been with C# and ASP.NET at the time. We were at the Borders Bookstore in Corvallis, Oregon, must’ve been Summer of 2009 — a playful, romantic season in the Northwest. I was deep into the Microsoft stack at the time, and (as you are aware) have the tattoo to prove it.

Really more of a brand than a tattoo

ASP.NET MVC was a new tool that came out I was totally into. Gone were the days of trying to manage HTML pages through a stupid GUI designer and ridiculous ViewState, I could simply ship HTML angle brackets to my browsers. A bizarrely novel idea, no doubt, but when I was informed by a colleague that ASP.NET MVC was simply a knock-off of Ruby on Rails implemented in C#, I scoffed:

“Those open source hippies couldn’t build anything like this!” — I exclaimed. “Microsoft is practical. We’re the ones with real jobs making money off real software.”

But my interest was piqued. I needed to meet these open source hippies. I blocked off a Saturday and spent it with you and Django.

Django vs Rails

I couldn’t put the book down. A model layer inferring it’s schema from the database? Migrations so I don’t have to write crazy SQL scripts when we deploy?

Dangerous code!

I was apprehensive, however. This new world of dynamic typing had me worried. “Someone could just have some code in a gem to turn + into -? How can anyone operate in this lawless world?” — I pondered.

The biggest surprise I had was that there were ways to make money writing code in open source. The code might be free, but I can still have a job! And one that pays just as well, if not more! (This was pre-SV craziness, remember)

We were off to the races. I bounced between you and Django. I chose Django at first. Django took me up to Seattle, then down to San Francisco to work at my first startup. The entire time, though, I was seeing you on the side.

A little side-project with you, Rails, was ecstasy. It was so much faster for me to work with you than Django! I did miss Django’s admin interface, and Python’s whitespace indentation, but you were just more complete. You had Gems too. Unbelievable Gems. You took programming from having to think into putting together a Lego set — and I mean new-school Legos that come half-built, not that ‘creative’ stuff I had as a kid.

Our Salad Days

It got even better when I ditched Django and took you on at work. I got to experience life on a modern development team. Pull requests (lgtm!), testing, continuous deployment, infrastructure automation, and GIT! It was a dream; I attribute it to you and your conventions. You had the right idea when it came to building web apps.

The conventions had a new benefit I wasn’t even aware of. I could jump into any Rails project and start contributing immediately! I knew exactly where to find the code and what the code would look like! Every new project was knowing how to fit all these Gems together into a cohesive application. Conventions worked.

The other great, yet unexpected, part of working with you was all the beginner support! I started with RailsBridge and Rails Girls to find I really enjoyed helping people first learn how to code! I even got to become a teacher at General Assembly! It was an amazing opportunity, and your conventions, style, documentation, and community made it happen. People really can learn how to code in just a few months with you.

Trouble in Asset Paradise

But then there was trouble. JavaScript came along, and although neither of us were fans, it was the reality of our environment. You tried your best, and I had your back. I was head over heels when you brought in Asset Pipeline. Finally we had a way to build up our CSS and JavaScript in a way that was seamless across all of the apps!

The whole adding the hash thing to the end of a filename? I think you made that up. At first, I didn’t see the point, but when I saw how easy it was to configure CloudFront to cache our assets indefinitely, I was a firm believer.

I credit you with bringing Sass to the table as well. CSS was so less painful now.

You also introduced me to CoffeeScript, and although we had an initial fling, after hanging around CoffeeScript I found that I certainly don’t prefer the company. CoffeeScript just gets in my way and the code is actually uglier than JavaScript once it gets hairy.

JavaScript kept getting in the way. You tried `remote: true` and it was terrible. You tried turbolinks and that was worse. The simplest applications we had failed hard using turbolinks for strange reasons. The client-side memory usage in our applications went through the roof if it did function.

Meanwhile, Asset Pipeline was becoming a burden. CSS compilation times went through the roof, and 20-minute deploys became not just an occurrence, but the norm. What was worse is that it kept breaking deploys! I have had more production issues with Asset Pipeline than probably anything else. Using it on Heroku was even a bear. Some of these issues are better now, but I still don’t like you screwing with my assets since I can get Gulp to do the same thing, faster, cleaner, and more flexibly. Remember that time I put a gzipped file in your assets folder and you refused to boot? Remember your fight with Bower?! Also, why do you need CSS assets to run your sidekiq jobs?

You simply didn’t understand my JavaScript needs.

Concurrency

You’re slow. I’m sorry, but you’re slow. I don’t mean in terms of CPU performance (although you are), but you’re pathetic when it comes to concurrency. You toss this mutex on that blocks anyone else from talking to you when you’re talking to someone else! I know the concept of concurrency is strange to you, so let me put it this way:

One process should serve many users.

With Node, I can get insane concurrency! Java, Node, Clojure, Go: They all get it. You even seemed like you started to get it too. I loved the idea of Ruby 1.9 fibers and how em-synchrony was going to transparently hide all this concurrency behind me. Where is the project now? Hasn’t been updated in almost 6 months: that’s where.

I’ve followed up on you. I know how to use you well and cram unicorns into a Heroku dyno, but I could only achieve 16k hits @ 252ms over one minute. Scala (which I had never used before) could do 84k hits @ 60ms over one minute. With no tuning.

This is where it got a little crazy, and I can’t blame you completely, but it came to a head when one of my favorite companies came under serious fire. I point a large part of the problem in your direction.

I think we’re just at different points in our lives. Concurrency matters to me now.

Why do your friends not seem to care? Why is there not a solution? I think I know the answer, but you’re not going to want to hear it. We don’t need you anymore. Those that do want reasonable concurrency have already left.

JavaScript Rising

I had to find a solution for our JavaScript. People wanted more interactivity. They wanted simpler forms — or no forms at all. I was terrible with JavaScript but needed to learn it. I needed a framework. I introduced myself to Ember and Backbone, but then found Angular.js, and we’ve been great ever since. You didn’t like Angular. You caused the problems.

Just take a look at this article that describes how to integrate you with Angular: https://www.honeybadger.io/blog/2013/12/11/beginners-guide-to-angular-js-rails. The first few paragraphs (and meme pic) is specifically how it will be hard. You were never hard! Why are you hard now?

Forgetting setup, you treat JavaScript like a second class citizen. It seriously pisses me off that you expect to put my Angular controller at /app/assets/javascripts/controllers/foobar.controller.js. That’s blatant JavaScript abuse. It’s just as much code as your /app/controllers. I realize this is just structure that could be resolved, but I point it out as an example of where JavaScript is poorly represented.

The JavaScript community hasn’t really landed on a solid module solution, but when they do, you better be on top of integrating it. //= is not a solution, by the way.

These aforementioned issues: they’re all solvable. I know you can change, and have in many ways. The problem here is more core. We don’t need you anymore.

Errors

You get errors wrong. With you, you want me to assume that my code will work every time. I can verify by writing tests (time consuming, but I do it), ad-hoc testing, but a disappointing amount of my bugs come out in production! On teams I have now, we usually dedicate full time pairs (that’s 2 developers, 40 hours/week for those counting at home) just to wrangle bugs (we call it the bug sheriff and deputy). It’s unreasonable, unenjoyable, and unwanted.

Node and Go might be a little more frustrating since they both force me to handle errors right away, but that forces me to think! “Oh shit, what would happen if that database write did fail right here.” It’s a slower process, and maybe I’m getting a little older, but I say deal with it up front.

Go in particular is fantastic at dealing with error situations.

Still, you could fix this. In fact, you don’t really need me to, I could just write my Rails code differently (although this breaks your precious ‘convention’). In a way it doesn’t matter, because we don’t need you anymore.

We Don’t Need You Anymore

If I build an Angular app, I just need an app to host a simple static page and a JSON API. You had all these great things for form-driven, back-end generated HTML pages, but I’m not building those anymore. My apps are harder to build: yes, but they are more interactive and have much better UX. I also get a mobile API for free. Your apps are stale and outdated. The only way your apps are good is if I break all of your conventions!

At the database layer, this is arguable. You do make my life easier when I’m using SQL, which should be no surprise. I believe nearly half of all of your code is solely in ActiveRecord. When I’m with Node, working with SQL is not good. I wonder how much that matters though. I’ve delivered many apps with alternative data stores and it’s gone better than with your SQL cousin. I’m not convinced I need SQL anymore, therefore I’m not convinced I need you anymore.

Let me illustrate: Here is just part of a Rails app that has a simple GET/POST JSON API: (there are actually >100 files in this project)

Rails JSON API w/ Mongo

And here is the equivalent Sinatra code:

Sinatra JSON API w/ Mongo

Some may argue that all that configuration is necessary to keep your app in line. After building countless Rails apps, I’m convinced that the MVC convention/configuration in Rails works great for web applications delivering HTML. It’s very poor for APIs. APIs always break Rails’ REST semantics — for just about every request. Also, I can have those same semantics in Sinatra, Express, or Martini with little effort.

You have bad conventions now. Turbolinks and Spring I have my students disable for every Rails app they make. It frustrates them because they don’t understand what this thing is I tell them to disable, but it’s worth not having them bang their head against a computer for hours not knowing that stopping the server doesn’t actually stop the server. Turbolinks is probably the most egregious part of the codebase. It’s optional, yes, but it hurts beginners more than anyone, and that’s a problem.

While we’re on the topic of bad conventions, how in the world did people decide to build strong parameters? It’s like this weird opposite world where people decided to write code to prevent things from happen instead of just doing normal work.

Let me illustrate, is this easier to understand?

Strong parameters example

Or is this?

Basic controller/model example

Ever try to make it work with an array? While we’re on that topic, ask any senior Rails developer what they think of accepts_nested_attributes_for. Ask them why you should never use default_scope. Ask about the bizarre issues that can crop up from polymorphic associations. Ask about STI. There are literal land mines in the codebase that take years to discover. They’re mistakes that are artifacts of a different world of web development. A world unlike today with server generated HTML, relational databases, progressive enhancement (aka handicapped JavaScript). You’re still good at those apps, but those are never the apps I build in 2014.

This won’t be easy, but let’s examine your other faults:

  • New validations often break existing functionality. Want to enforce a validation for a user but not an admin? Too bad.
  • N+1 queries abound. I’m convinced you encourage it.
  • Ever try to add a second database? Good luck.
  • Deployment (outside Heroku) is very difficult. This is mostly because of the concurrency problems, however.
  • Why is displaying validations so crufty? You had one job you were good at, and that was making forms for me!
  • Rails has solid conventions for handling CRUD, until you want to perform it in batch.
  • Backwards compatibility? Migrating from Rails 2.3 to Rails 3.0 was the single hardest task I’ve ever had. You’re a little older now, but still immature in this regard.
  • Asset pipeline bears mentioning again. It breaks things it shouldn’t. Does work it shouldn’t. Is way too slow, and is all around inflexible. I have dug through the sprockets code at length and it is way too complicated.
  • Callbacks. Beginners try to use them, but they usually just cause grief. Just put the code into the controller.
  • Websockets! They’re a great component of the modern web! Except I can’t use it with you without something like pusher and the Rube Goldberg architecture that comes along with it. Go ahead and try it with ws. You’ll see what you’re missing.
  • Fat models! Oh shit, now we have fat controllers! Damn it, bring in a service objects! Now I have 5 different classes to write, unit test, and dependency inject. None of these new interfaces are things my colleagues understand. Thanks OO. I wanted to take this API response and put it into this database, now you had me build the Taj Mahal. — I just can’t give you what you need.

I’m sure that’s hard to hear, but it’s for the best.

I’m building APIs now. APIs are so much simpler. I don’t need your HTML. I don’t need your ORM. I don’t need your conventions. These are the reasons I don’t need you.

What I Will Miss

I love you Rails, but I’m not in love with you. Some of your highlights:

Conventions. You had the best conventions a few years ago. All the right idea, and would easily solve technical discussions with one right way to do something. Looking into any codebase of a gem or an app was so easy to understand.

Gems. Sort of. Some are great, some are terrible. It’s so easy to pick up and go, but I’ve ran into enough problems that adding another line to my Gemfile is always met with hesitation. You’re not the only game in town, but you certainly set the standard.

Ruby. I may see Sinatra for a bit to keep Ruby syntax, but I wouldn’t get the concurrency I’m after. Ruby is so clean and Matz’s philosophy of designing for developer happiness pays continuous dividends. If we can stay friends, this might be the way to go?

The mailer setup is also pretty good. I like sidekiq a lot. Your ORM is world-class. I love that everyone you seem to know is a big tester. You’ve got some pros, but it isn’t going to change my mind. This is for the best.

My Future

I think we should see other people.

You should know I’ve been seeing Node, Go, and Clojure for quite some time. I have fallen in love with JavaScript, and things like generators and promises have only enhanced my feelings towards JavaScript. I’ve also found Node to be capable at random other tasks you could never achieve like Gulp or HTTP proxying. Node is big in enterprise, but they always hated you. I don’t understand enterprise, but if something is working for them, that gives me confidence that my little apps can grow to be large applications too.

Go is great as well. I’m a bit unsure of the static-typing semantics, but there is a pleasant feeling that my code seems to run the first time I try it. Go gets concurrency too. It’s baked into the language in a way I doubt you’ll ever achieve. I also love the OO semantics. It’s simple and limiting in the best way. It’s a language for systems development more than application development, but I think the concepts carry across for the type of web development I do.

Clojure seems like it could be the winner. I love functional programming. I love the same CSP semantics for concurrency as Go. However, I haven’t launched a large application with Clojure, and am a bit unsure about how workable a large functional application would be. I would seriously love to try, but there’s a big learning curve for me.

I apologize for doing this in an open forum, but I felt that others should be aware of your faults. You were a fantastic framework for a solid decade; I want that to be clear.

I would say there are plenty more fish in the sea, but…

I’m sorry Rails. It’s not you, it’s me and my client-side MVC.

--

--