Periodically someone on the Internet becomes aware of concurrency-oriented programming languages like Erlang or Scala, and climbs up the bell tower to sound the “is Ruby dying” bell. This topic came up on Parley recently. A few people asked me to post my reply publicly, so here it is, with some embellishments.
First though, a disclaimer of sorts: it’s perfectly reasonable to suspect me of having a bias here. After all, most of my work in the past ~7 years has been in Ruby. And I make a lot of money from books and screencasts about Ruby. So yeah. Bias.
I’m not a “Rubyist”, though. I’m a hacker. As I state below, I like to think that any implicit bias I have towards Ruby is because using it is pragmatic as well as fun, to a degree I haven’t found in any other language. That said, other languages are fun too. Honestly I’ve been looking for an excuse to use Clojure on a real project; something that just cries out this can’t be done, or even prototyped, as easily in Ruby. I’m sure those projects exist; one just hasn’t come across my desk recently.
Anyway, onwards to my original reply…
I regularly dive into other languages, and Ruby is still the most programmer-friendly mainstream language I’ve used, by a long shot. I think as long as programmer joy enters into the picture at all, Ruby will still be a contender, and the desire of developers to use Ruby will drive its implementations to be more performant. Also, if Matz ever gets around to baking higher-level concurrency features into the language I suspect they’ll be wonderfully well thought-out and easy to use, because Matz.
I do wonder if the focus on concurrency and scalability is a little overblown. There’s a natural bias, because it’s only the largest organizations that have incentives which drive them to create whole new languages like Go and Clojure to solve their massive problems. Then they naturally capture the spotlight, because they are big, and new tech is interesting, and massive problems are interesting, and “scaling a big system stories” are the supermarket checkout tabloid fodder of the programmer world. Everyone wants to have that amazing scaling story to tell.
Meanwhile, I suspect 80% of programmers are still working on problems where their development velocity is a much bigger problem than how many hits their server can take before falling over. I dunno, maybe my view of the industry is skewed. I just don’t think there are really that many developers, statistically speaking, who can cite system capacity as their current problem #1. Or #2, or #3.
It’s kind of like when we talked to Ilya Grigorik recently, and he was talking about how most companies are using lots of binary protocols to link their systems together internally. And James and I were like “um, I think you’re thinking of Google, not most companies”.
Another thing to keep in mind: the most important asset your team has is your shared understanding of the problem. There are lots of great scaling stories out there that don’t involve replacing the language; they just involve quickly rewriting a major component to have a more performant design once the team had a better handle on the problem space. It’s easy to overestimate how much time you’ll save by making “scalable” choices up front, ignoring the fact that most successful system eventually experience a rewrite or three regardless. Maybe that rewrite happens to be in Clojure or Elixir because with your improved understanding, you realize how you could use their special features to great effect.
Final thoughts – you should, of course, use the best tool for the job. In my book that doesn’t mean pondering long and hard over what the right tool will be a year from now. It means whipping out my friendly pocket Leatherman tool, and only hauling the big toolbox once I’ve satisfied myself that the Leatherman is insufficient. My Leatherman happens to be Ruby.
So that’s my original reply. James Edward Gray then piped up with the following anecdote:
This is just one datapoint, but…
- I currently work on an SOA system that’s about 30 processes talking to each other
- We aired a 70 spot TV ad earlier this week in Germany (our primary market)
- The publicity pushed our peek performance up to 40,000 requests per minute
- This system is all Ruby, except for a tiny Node.js frontend
I won’t say we didn’t see problems at this scale. We did. But it held up, even running at those speeds.
I’m now testing a set of changes based on the things we saw during the traffic peek. I’ll have it ready to deploy in another day or two tops and it will drastically speed the system up from where we already are. In the worst cases, I’m seeing tasks take about 3% of the previous time.
This is real world production code. We’re just finding bottlenecks and fixing them, like you do.
Also, we’re just using MRI.
I think there’s a kind of peer-pressure when you’re deeply embedded in the developer community to switch to the new hotness or fall woefully behind. For instance, I feel like I really ought to be studying Backbone, Angular or Ember right now. The other day I wrote a single-page realtime chat app for fun, and just assumed that I’d need to plug in one of those frameworks once I got to the client-side part. As it turned out, all I wound up needing was a few lines of jQuery.
The important thing to keep in mind is that [nearly] every new hotness was developed to solve someone’s specific problem. And if it’s really, really hot, that’s probably because it solved a really, really hard problem. If you have the same problem, it behooves you to sit up and take note. But bear in mind that just because your CMS app delivers data via HTTP and someone else’s realtime statistics visualization app also delivers data via HTTP, does not mean you have the “same problem”.
When I programmed in Delphi, we did it because Object Pascal was the best option around at the time. The fact that it was ridiculously fast was a bonus, but came to be something that we began to understand the value of as time went on. It wasn’t an initial requirement, but became an essential feature.
I think that concurrency and the safety and speed of typed and compiled languages feels optional right now, but I maintain that 5+ years from now, having generated new systems in a single-threaded (essentially) language that does not make concurrency a core feature, in 2014, will have felt like a bad decision. Like a feature that we thought was optional, but was actually core to the longevity of the system.
Then there is the value of community. The Ruby community is truly great, but there is so much more energy in the Node and Scala and Closure — and even Golang — communities. They feel like Ruby did 5 to 10 years ago. Ruby should have been forked and improved to support concurrency as a first-class concern, by now.
I don’t think that, if I am still writing code in 5 years, that it will be in Ruby. And that makes me wonder what I am waiting for to adopt a more powerful language.
I try not to make decisions based on 5 year projections about technology.
In five years, we’ll be driving our flying cars to work. We’ll then be able to rewrite our Ruby apps to _____ with all of the commuting time saved.
You talk about Ruby being a single-threaded language, then namedrop Node? Ruby supports parallel execution across multiple CPU cores with JRuby. Node is single-threaded.
Avdi, I’ve read your stuff and watched your screen casts for a long time now and this is the first time that it just really didn’t sit well with me. The tone seems dismissive, including your reply to Mike below. I 100% agree that scaling stories sell well (including the one you included from @jeg). Where I think you lose me is that hitting a performance wall isn’t always a case of just a quick algorithm change or rewriting less of performant code, particularly when the language itself has limitations that exclude certain options. And not all applications are so trivial that performance doesn’t matter. Ruby has grown up considerably in the years that I’ve been using it but for many it is still considered the new hotness. I think we as a community have a good understanding of where Ruby is good and where it’s not and when to use something else.
I think part of the problem for me is that I don’t think most non-trivial scaling problems would have been addressed in any useful way by starting with a difference language. Twitter structured their messaging system as a CMS originally. Low and behold, it scaled badly, because they didn’t originally understand what sort of system they were building. They would have had the same problem if they’d written a CMS in Scala. They probably also would have gotten to market slower, because they didn’t know Scala.
If I sound dismissive I think it’s less because I like Ruby and more because the older I get the less I think language matters. If you are lucky enough to have a hit on your hands then you WILL rewrite eventually. Yes, it’s good to have a good handle on what’s out there, because when you do the rewrite you may want to do it in a more suitable language with your improved understanding of the domain.
But if you jump on something bleeding-edge, your chances of even HAVING that problem in the first place are reduced, because of the added friction of lower familiarity and a less-evolved ecosystem. Recently I tried to write some software for my business in Elixir. I wasted weeks because of the horrendous condition of internet libraries in the Erlang ecosystem. I was spending all my time helping a library author get all the bugs out of his HTTP library instead of solving my problems.
It is essential, as a working developer, to have at least some handle on what’s new in language design. But personally I think it’s focusing on the wrong thing to imagine that choice of language on day one is going to make or break a project—except in the case where you select an immature language whose limits you don’t know, in which case that just might break it.
You might say this is just me getting old, but back in 2001 I wasn’t suggesting Ruby for the mission-critical stuff I was working on. I was using it for developer automation, for which it was admirably suited even back then.
Agreed. Performance != Scalability. Ruby is fast enough for the most common business problems. If you happen to need good concurrency from day 1 then personally I would look elsewhere because despite Ruby having arguably adequate primitives, having it as a core value in the language and surrounding community such as in Erlang, Node and Go is going to give you a lot of mileage in a difficult area; analogously to how ruby gives you a lot of mileage in the general structuring of an object-oriented codebase.
I’m assuming by “concurrency” you’re referring to things like threads or processes that pass data around.
My observations of other’s code as well as my own, is that concurrency is hard. Reasoning about it, thinking of how it can go wrong and understanding how it’ll perform is very tough. In fact, I’d wager there isn’t a human alive who can do it.
Switching your tool (e.g. language) to solve it is a reasonable solution. But using two languages side by side can be tricky (though some play well together, e.g. jruby and scala).
Good code discipline can help with the concurrency problem; it can minimize the surface area where problems can occur. But there will always be that really hard to think about part… and that’ll be where the bugs appear.
I think a neat solution for this in ruby would be a “reduced” version of ruby; Either via some meta-programming or an alternative compiler/interpreter. This reduced version of ruby would be the only place you could actually do concurrency. You would use this for the hard-to-reason-about areas and it would provide some of the guarantees that languages like erlang provide for concurrency.
Anyway, this is just what I was thinking about as I read your article.
Ciao!
In our real-world environment we solve scalability problems in Scala (streaming video, concurrency especially with Akka) and user-facing problems (website) in Ruby. I write a lot of tooling in Ruby too, because while I’m pretty productive in Scala, I’m still at least twice* as productive in Ruby.
Any rumors of Ruby nearing its end are greatly exaggerated.
(* or more, depending on how ornery scalac wants to be about the type system on any particular day)
It’s difficult (at this stage) to imagine a better language for tooling. Maybe someday someone will come up with a language which is much an improvement on Ruby as Ruby was on Perl, but I haven’t seen anything approaching that yet.
+1. Our backend team needed a companion client framework for Rails, tried out Angular, but then fell in love with React. Confident developers use the right tool for the right job with the right people.
http://javascriptjabber.com/073-jsj-react-with-pete-hunt-and-jordan-walke/
@avdi:disqus, from now on I’ll be using your NoneSuch gem with RESTful NoSQL concurrency to solve all my scaling problems 😀
http://www.virtuouscode.com/2011/02/18/nonesuch-the-greatest-ruby-gem-evar/
I think the best point is “use what makes you most productive today“. Many businesses won’t be here in 5 years’ time, and if your business reaches a point where scalability actually becomes a concern, you’ll probably have enough time to deal with that. But It seems absurd to me to start with big scalability concerns when most of the time you haven’t even proven that your business idea will work.
That said, I program in Ruby at work, but my heart lies with Clojure (and concurrency is the least important aspect of it to me). We can all still be friends, can’t we?
I agree, to some degree. It’s not all black & white and while Ruby works for me in most cases it doesn’t in some. I’d say it’s a “general purpose language to a degree” 😉 Actually this comment was initially very long so I just continued here: http://ku1ik.com/2014/02/24/rumors-of-rubys-demise-are-exaggerated-indeed-but.html
I believe you are completely missing the point of both this article and the comments here.
Let’s keep it up constructive, thanks!
My comment was meant as a welcome to re-read the article once again and rethink it’s meaning, rather than being a criticism. Sorry if I was misunderstood 🙂 (oh irony!)
Working at Engine Yard, we work with many types of apps from small- to large-scale. We’ve seen well-written apps and poorly-written ones. Majority of scaling issues has not been due to the language that is used but other areas, like database bottlenecks or single-point-of-failure. These are the areas that should receive the focus first when discussing scaling issues only after these types of issues have been addressed would it make sense to focus on the language.
Agree that it’s all about right tool for the job. Scaling is about architecture and design, and less about the raw performance of the language.
We can give another data point in the pro-Ruby column. We operate a image processing service and it’s all Ruby backed. We recently reached 8.5 Million+ images in a single hour.
Our ability to scale to this level successfully (and capacity to handle more) was due to our platform design, not the speed of Ruby. In most modern environments, processes don’t need to be C fast, they simply need to parallelize well. Extra machine power is cheap, readily available, and fast to spin up.
Isn’t poor concurrency and parallelism the main reason developers look beyond Ruby?
I think focusing on concurrency or scalability of Ruby is the wrong angle. Eventually I’ll expand this into a blog post, but the real issue that is causing me to move on from Ruby isn’t “Ruby can’t scale,” it’s “Ruby needs to be covered in unit tests just to be maintainable.”
I’ve spent most of the past few years maintaining fat legacy Rails 2 apps, and most of them have no tests whatsoever. It’s really demoralizing to have no confidence in your changes without having to invest a ton of energy just to set up a skeletal testing framework to write a single failing unit test for a simple bug or feature (and then you just have a bit more confidence in your change since test coverage is still near zero).
I can’t totally blame the people who wrote the apps I maintain. Just learning how to properly test object-oriented applications is a daunting task that requires a huge time investment. It’s really a whole industry unto itself, and you have to learn a new language/patterns to understand it (mocks, stubs, doubles, fakes, spies, etc).
Then, when you feel like you have a solid enough grasp on it and it comes time to actually implement the tests, everyone has their own slightly different way of doing it. Some people use RSpec, some people Test::Unit, MiniTest, some use factories, others use fixtures, whatever. Being flexible enough to accommodate these differences when you’re jumping between apps is another time investment.
I keep picturing the XKCD comic about just using the quick and easy goto statement (http://xkcd.com/292/), except in Ruby’s case the quick and easy fix is to not follow TDD and write a feature without a unit test. Except instead of you getting hit by a velociraptor immediately, it actually gets let loose to roam around inside your app (Jurassic Park?) for either you or a future maintainer to get pounced on later. It just requires a really high bar of constant vigilance and discipline to maintain that most Ruby applications will inevitably not meet.
As for me, I’m slowly moving on to Haskell. Although I’m still really early on into it, I feel like the type system (that you get for free and that every programmer must conform to) combined with its purity almost completely obviates the need for unit tests à la Ruby.
All that said I don’t think Ruby is going anywhere anytime soon. It’s got tons of momentum behind it (there’s a gem for almost everything) and I still think – as you point out – it’s a fun language, with a really low bar and a welcoming community around it. And it’s going to continue to pay my bills for the foreseeable future!
Every code should be covered by tests to be maintainable. Maybe with static typed and functional languages, some kind of tests are not needed, but even then your test suite should be able to feel comfortable that a change is not breaking something somewhere else.
While I agree all code needs to be surrounded with unit tests, I still feel the testing requirements for Ruby in a large application outweigh it’s benefits. I’ve been dubious of the productivity claims anyway but perhaps I’ve got so much experience in other languages that I’m not able to be impartial.
I guess it is the age old battle between static vs dynamic languages. For serious large applications I prefer static. Now for scripting/devops stuff Ruby seems awesome especially when you can almost create your own “meta” language with it. So I’m not saying Ruby is worthless. I’m just saying that for big code bases I wouldn’t want it to be Ruby.
I think the Ruby World is just all waiting for Matz’s higher-level concurrency features into the language just to settle this scalability problem.
I really wish James would explain their System more, and what problems he had, how he then got to tasks take about 3% of the previous time.
And recently i heard even the BBC in UK are using JRuby in production.
Then there is the absolutely huge, largest Ruby and Rails in production by any account from China Rails Ticket system. Far larger then even Github or Basecamp, combined.
The problem is we dont see many of how these are done being shared. Knowing that it scale is good. But talks and experience sharing will be even better.
You might be interested to search for talks on Cookpad. They have talked extensively over the years how they have scaled a very large Rails app, from application-level concerns to operations. Many such talks were at RubyKaigi, they may or may not be available online and in English but slides often are.
Thanks for taking a position. I think your post was evenhanded, fair, and correct. My company runs a high-throughput site built on ruby (and Rails) with a Solr backend so we must deal with the problems of scale, concurrency and capacity handling.
I would like to add one other thought.
There are indeed cases where threading and inherently parallelizable tasks are desirable. A bot, for example.
But of all the valid cases of deciding to use threads, spawn processes, running tasks in the background, parallel tasks or the same task on multiple instances, the vast majority are unnecessary and create complicated systems that are difficult to get right, and difficult to debug when they fail.
A common case of “unnecessary” occurs when running tasks in parallel doesn’t actually speed things up. Systems typically rely on some shared resource — perhaps a database, a network, or in our case a search index. There’s a point at which the extra processing needed to manage parallel tasks creates inefficiencies (and, of course complexity).
Consider for example a process that writes to a database. When you’re writing 10K records, it goes much faster if you commit them all as a batch, right? Not so fast: if you have 10 processes doing this, then depending on the locking strategy of the database, processes 2-10 could be blocking on the uncommitted records, or managing them in some way that results in double writes and so on.
This problem is not an issue with language; it’s an issue with systems design, data design, and architecture. And that’s hard in any language.
Make it work, make it beautiful, make it fast. The first two usually obviate the need to spend time on the third.
I’m not a ruby guy, but from everything I know about ruby it is a great technology for solving many kinds of problems. Are there things that you would be better of doing in Erlang, Haskell, Prolog or something else? Sure, but in many cases having a tool that your team understands is more important than what the tool is
شركة
تنظيف بجدة