Ruby Is Still a Diamond
medium.com
medium.com
> Many systems using other “preferred languages” also implement some form of GIL (e.g., Javascript’s V8 machine, CPython); we don’t hear complaints about it, because these languages have a rapport for heavy lifting and parallel tasks.
GIL is the biggest performance obstacle for python and is constantly discussed. JavaScript is notorious for being exclusively single threaded (unless you consider the very late arrival of workers to be true multithreading).
A single thread of execution for scripts isn't so bad when those scripts are all relatively small callbacks.
I'd like to know more about what you would have liked to see in an article like this, it is a bit of a fluff piece; the goal wasn't to provide a super technical explanation. The goal was to get people talking about Ruby 3 and I am happy to see so much discourse here! But yeah, I was asked by the maintainer of the blog to try to provide "layman's explanations" when I could, which I think may have kneecapped me a bit in retrospect - but obviously layman's explanations usually are best when they are succinct and accurate. Is there anything else, particularly in the layman's examples for parallelism and concurrency, that felt inaccurate or misleading? I want to make sure I am communicating the concepts and implications correctly.
From my perspective, the goal of the article is provide an explanation of concurrency/parallelism, so that when I begin to describe the features in Ruby 3.0 we have appropriate context to understand why fibers/ractors matter, so if I didn't do that I really bungled it!
Small nits: the image with fibonacci performance numbers needs a maximum number of significant digits. I think "implement" usually applies to an algorithm or idea; you might mean "use" Sidekiq? The thing I'm most curious about is how easy it is to write an extension in C/C++/Rust for Ruby (in comparison to cffi, pybind11, etc.).
Best of luck! It's not easy to write a technical article and face criticism, but I appreciated reading it.
Articles I trust are where I trust the author knows what they're talking about and doesn't assume things they don't know.
That said, for me the issue was that the introduction read like "this is going to explain the technical reasons why Ruby 3 rocks" and each section/bit thereafter was ostensibly "technical reason x/y/z why this is better" but then no section really followed up to the promise of actually providing any hard technical details (this is aside from the inaccuracies about other languages).
So, for me at least, it was about expectations not matching the content. I'm not a Ruby dev, but I am a programming language internals fanboy and I was excited to get a technical peek into what a hard-core Ruby user was saying had changed but found myself really stymied/disappointed that it seemed like the same bullet points were being repeated but with no technical content getting delivered.
TBH, it's really hard to write for nerds like us. I'm sure if I'd organically discovered your post, perhaps while Googling something you wrote about or if shared from a Ruby devs' group, I think I'd have come away with a different conclusion from the article. But seeing it featured here on HN played a role in making me think it would have juicy technical content which kept being referenced but then kept just out of arm's reach.
Thanks for trying, though! Sorry for being grumpy.
Honestly anything to improve performance with large python applications seemed like a hack - gevent, Gunicorn, uvloop, switching libraries to cython etc. It delayed our product rollout and increased costs(incl. cloud infrastructure). Hence I switched to Go for anything where performance matters.
Good to know that Ruby3.0 has made some improvements regarding concurrency/parallelism.
Sure, but this concurrency discussion is talking about green threads / event loops as much as true parallelism.
Really? At my company it seems like 90% of the tests are catching things that would be caught automatically if we had used a proper language with strong types/compiler.
IMO Ruby is slow, not particularly beautiful (yes, this is subjective), and seems optimized for shoving crappy REST endpoints out the door as quickly as possible.
Also, it seems like a very small team behind the language. Is longevity an issue?
Your questions point out the obvious shortcomings of Crystal currently. While it is easy to find libraries, they are few and their support is questionable. Not to do anything with the language itself or the ability of community, just the nature of a new and small ecosystem.
The team is small but I take solace in the fact that they have been at this for a very long time now (2011).
Once nicety that helps the ecosystem is the easy of interoperability with C-libraries. Many shards are simple wrappers around an already established C library.
I haven't looked at other languages seriously - at the moment Elixir and Crystal seem to be the most interesting to me
And if those bugs would be prohibited by static typing, then it sounds like Ruby is creating work.
> if we are passing a string instead of an array of strings somewhere, the test will fail
Is this an example of a real bug? It's mind boggling that anyone would use a language that allows them to type that bug into existence without an immediate error.
I know we used to be forced to use JS, but not we even have TS so that we don't have to catch extremely machine-catchable errors in tests or, worse, production.
It sounds like the tests are testing the wrong thing
> seems optimized for shoving crappy REST endpoints out the door as quickly as possible
Ruby is a general purpose language. Perhaps it is Rails that you have gripes with?
I'd also like to hear what you are doing that leads you to the conclusion that 'ruby is slow'. For most domains I would say ruby is fast enough to solve the problem in a way that customers are perfectly happy with.
I would argue 90% of the software businesses I've seen are simply CRUD REST endpoints. If that means you can get those out the door as quickly as possible, why on earth would you want to use anything else?
It's almost like you're actually advocating for it...
If I'm in a situation where I need to throw up a rest server as soon as possible, I'm probably using NodeJS. It's dirty but it works ( except when you have the wrong Babel compiler installed and lose 3 days of your life trying to fix it)
Ruby's fun to write but I hope never to onboard to a Rails codebase again. I think the vast majority of the ones in the wild are... not great, to put it mildly.
It's not just the framework, but the fact that have proper tests, architecture, and CI/CD.
I feel like in my career, across many different languages and platforms, "legacy" codebases (ie, any codebase that pre-dates me and is a non-trivial amount of code) are uniformly "not great to put it mildly".
1) The amount of "magic" available and how much it's used—in low-magic scripting languages or frameworks, you can at least grep to find definitions, even if better code navigation tools aren't available. The more often the answer the "wtf is this?" can't be known for-sure until runtime, the harder it is to find your way around, and the more memorization you have to do. Rails and its ecosystem are very high-magic, even more so than something like the roughly-comparable Django. It can be annoying to even figure out which gem defines a given symbol, in a given context.
2) The level of reliance on tests to keep things from falling completely apart. Tests are generally good things, of course, regardless of language, but a poorly-maintained test suite in a Rails codebase is a far more grave problem than it would be in, say, a Go or Typescript codebase (yes, it's mainly the static typing that makes the difference). That is, keeping the codebase viable requires more active attention and work.
This is exacerbated by Rails being a common choice for bootstrapping startups (the library ecosystem, especially Devise, which might be single-handedly the main reason Rails wins as many "what should we use?" discussions as it still does, is a pretty damn strong argument in its favor) and bootstrapping startups having a tendency to go through multiple teams in short order (contractors and external dev teams being common, both in the early days and in the somewhat-later, very common, "oh god the cut-rate shop we got to build this fucked it up, please save us" phase) means that a whole lot of live Rails codebases have been treated very poorly, and that, for the above reasons, a Rails codebase that's been treated very poorly is a special kind of hell.
That said, different usecases require different languages. I would never use Ruby for a compiler project.
And I'm not a Rails person, so it wasn't flattery.
I have friends everywhere from FAANGMULA to Deloitte to 100 person tech startups (and I am at one myself)to banks. We have a group chat of classmates.
No attempts to stem the wave of resignations anywhere as far as we can tell.
As things have played out, PHP has done pretty well at modernizing (I know some would debate that, but that's a different thread), and Rails has faded a bit into the background -- although now I joke that because Rails is well out of its fad phase, it can get down to the real work.
I do wonder whether Rails has been kind of a mixed blessing for Ruby all in all, though. I think Ruby is, in itself, a great alternative to Python or Perl, and it's the language I reach for first when I'm writing short utility scripts -- but since Rails exploded in popularity before Ruby itself did, a lot of Ruby programmers really only know Ruby to the degree they know Rails.
This so bad that frequently I see “Ruby on Rails” in lists of programming languages aside “PHP”, or “Elixir”, or “JavaScript”.
(When I was most in to Ruby – but not so much Rails, even then – there was actually a palpable divide in the community between the Rails-only community and the broader Ruby community, some of which was Rails-inclusive.)
Maybe in USA. In Europe, Rails has been far less popular. Now I imagine expensive candidates means the ability to work on decade old Rails codebases from previous versions, which means with a lot of Rails experience.
Half the companies using it are 1-2 years old according to him. Lots of 10 person startup teams just fresh off a raise.
Given Google's track record I'm not sure that's the most reassuring argument...
The Ruby and Rails ecosystem has Stripe, Shopify, GitHub actively pushing new things all the time, DHH and co working on version 7 right now, etc...
class String
def colorize(color_code)
"\e[#{color_code}m#{self}\e[0m"
end
end
The general advice is to avoid confusing others though, so changing base classes like this is not really a business-safe idea.But this is true of any language. The truth is in the middle ground.
In Java, for example, one can do static analysis and identify when a class is doing reflection... and flag as an error if the class is doing reflection on a class that is an instance of java.something.
Class cache = Integer.class.getDeclaredClasses()[0];
Field c = cache.getDeclaredField("cache");
c.setAccessible(true);
I can examine to see that java.lang.{something} class is getting reflected on. I can check to see if anything is calling getDeclaredField and flag that. I can see the setAccessible and really flag that.This isn't about coding errors. This is about being able to analyze the code and see if the code is using prohibited methods.
Through static analysis, I can verify if anyone is trying to do something tricky with the String class.
Can I verify if a method is being modified added to an instance of a String in Ruby through static analysis?
Trying to make such experiments safe in Ruby would be unwise, but starting them in Ruby is happiness.
The drawback in this case is it's not as concise as just typing in a String literal :)
Even still, this doesn't mean anyone should drop what they're doing and migrate all their code to Rust! If you're a Ruby dev (especially if you do Rails) and you're making a living churning out apps with your good old friends like Devise, RSpec, etc. Rust is probably not the thing that'll really wow you. It _can_ do web, and do it REALLY well. But the experience is very different. (Actix/Rocket)
I'm not a C programmer, but I think Rust can fit almost everywhere C can fit.
I think this should be phrased differently. There is a minor bloom of zero-allocation web frameworks in go and java, and probably a few others, which blow their peers out of the water and are horribly not at all idiomatic usages of the languages.
They can be very fast, but the performance cliff for using idiomatic code is very significant.
Does idiomatic rust perform VERY fast? Can it be both very safe AND very performant? I think the answer to both of those is yes, but I honestly haven't used it enough to know.
Honestly I kind of cringe at the "do everything in rust" crowd, if you need to, say, write an API, you can just do that in Python or Ruby or Go, 99% of the times the performance penalty won't matter and Rust is just a very complicated tool for that job.
But it single-handedly reopened the discussion on high performance, safe(r) languages, and I believe that's a very good thing even though I don't use Rust day-to-day. Projects like Zig are in a sense following in the footsteps of Rust.
total = new Decimal(principal);
total.mul(1.0 + interest);
In other words decimals support the same operations as the rest of the language's numeric tower. Go doesn't have this.Go is a networking language. It's good for writing microservices and network protocol clients and servers. That's about it.
Python is also great but I still wish Ruby is installed everywhere for writing one-liner since Python isn't good for that.
Ruby is ridiculously useful. Python edges it out for scientific/engineering applications because of Scipy and now Machine Learning ecosystem.
One of the things I love about Ruby is that you can do very powerful things with minimal code, while still having it be understandable. The way metaprogramming works in particular is fantastic. I also like that Ruby provides an escape hatch to do horrible but necessary things (backticks), which lets me utilize existing hacky scripts and build upon them, without needing to rewrite everything to include it.
You missed an opportunity for a amazing pun :-)
One thing I like about both languages is how nice it is to write tests. The patching and the assertions are great.
Crystal lang does a bunch of great thing but the poor support on Windows (having to install some Linux subsystem to compile stuff) is a non starter for many companies.
I personnally never liked Python's syntax, or OOP, it feels tacked on. However Python's ecosystem is just too huge to be ignored.
i.e. if True: a = 1 if True else 0
> Oh and how I miss one line if statements and conditional assignment when I'm in Python.
derac provided an example exercising both. To separate them:
One line if statements:
if predicate: expression
Conditional assignment: a = 1 if predicate else 0
The else is to permit the conditional assignment, it's a ternary operation, it is not for the if statement. launch_nuclear_weapons if at_total_war
Just reads better because it doesn't look like a mistaken if statement.By conditional assignment, I meant more something like this:
some_var ||= 45
And I know I can do either: some_var = some_var or 45
some_var = some_var if some_var else 45
But it just looks stupid and it is such a frequent need due to the way default args work for things like dicts, that it gets annoying.This was changed in 3.0. String literals are now frozen/immutable by default.
There was a plan to do that at some point, but it was pulled back from for backwards compat reasons.
You can opt-in to string literals being frozen by default on a per-source-file basis with a magic pragma comment, and this has existed for several ruby 2.x versions and is unchanged in the released ruby 3.0.
As an outsider, really curious to hear more about this. Is it from an ecosystem point of view, where the "dynamism is disappearing" or more from a day to day, the fact that writing TS is significantly different than writing JS? Or even something else?
You also lose the "type out code really quickly without thinking too hard about it" and the thrill of wondering if it'll work when you load the page.
Needless to say, my scripting nowadays is almost exclusively Typescript.
I enjoy ruby, & it's nice once you get that every property access is a method call ("it's all message passing") which has nice dynamism (see Sequel)
Maybe you can argue Ruby does a better job not leaking its abstractions (no __slots__ etc)
If I need to write something, like a small tool, or an API, or a Prometheus exporter (https://github.com/robotmay/prometheus-aanet-exporter/blob/m...), I write it in Ruby. The Prometheus exporter is a good example actually: nobody else seems to write them in Ruby, but I found it easier to make one from scratch than to figure out the pretty obtuse and non-standardised examples written in Go by everybody else. Obviously this won't be true for everyone else, we all have our own favourite languages, but for me it's very rare that I find something that can't be written effectively in Ruby, so I'm happy to keep writing code that way.
There's always a lot of arguments about Ruby/Rails performance, but it's actually not too tricky to make it run fast enough for most tasks. It is fairly easy to shoot yourself in the foot I guess, compared to other languages which are natively fast, but there's a downside to all languages.
I really really didn't like Ruby when I tried to learn it at work , but Python just clicked. I imagine many Ruby programers migrated to Python.
Then again, Nodejs started to take off when Ruby declined. I strongly suspect this is due to all the "full stack" positions. Hey Billy, you write all that nice js on the front end, now you can write it on the back end too.
Not at all, they used to be competitors in the web space but Python won the academia space. Python is taught at university in Europe, Ruby far less.
Node.js is a mess, not because of javascript but all these layers of complexity imposed by professional front-end development, and on the server because Node.js standard library is pretty poor. I shouldn't have to use a third party library just to parse a multipart http request or work with cookies...
Go in that domain is just examplary. The std lib even comes with a Go parser, which is just invaluable for implementing code analysis tools, and it's very easy to do so.
Not my experience being involved with the community. Indeed, there seems to be rather less migration between the two communities than than other languages (e.g. Python + Go, or Ruby + Elixir) probably by virtue of their similar problem spaces and approaches.
I'm a Rubyist who also knows some Python and while I admire Python, I certainly prefer Ruby. Python is flexible and fantastic in many ways, but is a far less consistent language in terms of organization (e.g. you use methods for some things, functions for others) which didn't click with me. If you want to do something with an array/list in Ruby, say, 95% of the time it's "arr.method" whereas in Python it might be len(arr) or arr.method, a comprehension, etc.
The coolest thing about Ruby is that the language paradigm is very internally consistent - everything, absolutely everything, is just objects and methods. The language has very few core constructs, but they can be composed together very fluidly to create all kinds of rich abstractions. People who dislike Ruby generally dislike this specific aspect -- it tempts programmers sitting atop mount stupid to go nuts with meta programming and try to make "magical" code when it isn't called for, and that's annoying to deal with on a team.
By comparison, in Python there are multiple paradigms and you need to learn where they apply. Why are things like `map` and `len` global functions, and why shouldn't we just call `__len__` on that object ourselves? Why do some functions for list processing take a list first and then a lambda, and some do it the other way? Why do you have to define self as the first argument for methods but you then just ignore that argument when calling the method? Why is it so hard to perform a relative import? Python is less consistent.
The other nice thing about Ruby is the elegance of it's closures and block passing - I miss that a lot when it's not present in other languages, and am always excited to see new(ish) languages like Kotlin embrace that paradigm. That, combined with the enumerable interface, make for great functional programming and list processing in Ruby. The alternatives in Python are certainly functional, but feel clunky to me.
On the other hand, Python has better semantics around named versus positional arguments, better type annotations, and decorators, each of which I would love to see in Ruby but for many reasons don't expect to. Python's import system allows more control over the global namespace, which is very nice. And lastly, Python has become the de-facto academic language which has led to it's fantastic ecosystem growth. Python is everywhere and there's a python driver or library for everything. Ruby has a good ecosystem, but not near as good as Python.
As for the bit about Node, the rise of Node and relative decline of Ruby are the same phenomenon, it was the full-stack web hipsters that had powered the rapid rise of Ruby moving onto Node that helped Node rise quickly as well. There were valid reasons, specifically 99% of Ruby developers also need to be proficient in Javascript, but the two languages are quite different, and many engineers felt (reasonably) that it would be nice to just pick one and have it work across the stack. Well, the browser doesn't run Ruby. Also, Node's evented model is a good fit for precisely the kind of IO-bound stuff that's a bit harder in Ruby.
But mostly, the hot-new-thing wave follows a pattern of enthusiastic adoption and evangelism, then over-adoption, followed by cynicism when the things that were built in hotlang don't scale up as well as their enthusiastic early adopters hoped, followed by the embrace of next hotlang and the trashing of *previous hotlang. So Ruby had to ride that out and calm back down into just being a normal programming language again. Meanwhile here comes Deno... ;)
I've lost many hours trying to get relative imports to work. It might be the one thing about Python I absolutely hate.
But Node doesn't do this well either, maybe it's just the nature of dynamically typed languages. The two programming languages I enjoy the most would probably be C# and Dart / Flutter. Having types, like real types is essential when projects scale. However I only get to enjoy these programming languages in my spare time.
Personally I think Dart is everything I could ever want in a language. Being able to use types when I need to, and the freedom to use Dynamics when I just don't feel like creating a new class. If you haven't tried Dart yet it's like Java's younger sibling who's easier to get along with.
Here's a server side Dart framework
Not sure how mature it is, but if I ever have a need to build another backend server I'll try it. I tend to prefer managed solutions now like Google's Firebase, which integrates so well with Flutter ( obviously because Google has a vested interest in you using their services)
https://stablekernel.com/article/announcing-the-sunsetting-o...
I spent a bit of time looking for another project, but it looks like they're just isn't a whole lot of movement on this
Subjectively, I’ve been using Python since version 1.5? on classic Mac OS — but I’ve never liked it much. Always feels like a struggle to do basic flow control. Ruby really clicked for me. The ‘_why the lucky stiff’ quote really resonates: it’s the language Buddha would have programmed in.
I'm using the perspective of the whole programming language landscape. Take Python and Ruby and sit them next to Haskell, or SQL, or Prolog, or best yet, all of them at once. Even within just the "dynamically typed languages" they are the most similar to each other.
Syntax is one of their greater differences when sat next to each other, but much less impressively different when considered in the whole landscape. Syntax glosses on virtually identical semantics.
Things that came more from Perl to ruby include: Ruby's syntax; first-class regex support; some rarely used features like the Perl-ish global variables and command-line options; just the idea of being an interpreted unix-friendly language executed like `ruby file.rb`.
I do think Python and ruby are very similar languages, in the whole universe of languages. I think that's not really because either influenced each other, but instead because both were invented at approximately the same time, to meet similar needs -- both as interpreted-not-compiled, edit-with-a-text-editor, with C-like syntax, etc.
Them both being so similar, I wonder what's behind things like "ruby just didn't click for me but then I loved Python" -- but then, I'm the same just opposite. I don't know if I'd say it didn't "click", but I much prefer ruby to python, hugely (and sadly at this point, I feel like a betamax enthusiast), despite knowing that they are basically similar languages.
Such as end and lower casing variables.
Another factor to mention, Python has some specific use cases, like machine learning which I can only effectively learn by using Python. I actually managed to secure a 30k pay raise by learning Python in my spare time.
Anything I can do in Ruby, I can already do in NodeJS. If I ever run across an employer who would offer 30k or 40k more for me to learn Ruby, I would definitely jump at the opportunity.
It's just a great skeleton Rails app to begin with. You'll be able to start on the hard problems much sooner.
Adding threads complicates the interpreter, to the point where I'm beginning to doubt it's worth the effort.
For my own interpreters I've had success running separate interpreter instances in their own threads and communicating via channels. This allows multi-threading without making a mess.
I don't have an example of that I'd call successful, where here I'm defining "success" as "the entire community casually uses it whenever it is useful and there are no special warnings to be given about the threading support". There are some implementations, but last I knew they all tend to be sort of dangerous and things the community will warn you away from, and some languages just don't have them.
I would be intrigued to see someone try to create a modern dynamically-typed language or interpreter written from the beginning to be threadable, but it seems to me the energy in the dynamic typing community may not be there any more to sustain it. While it's not "dying", I suspect that as static typing is getting better the dynamic typing community is, shall we say, losing the updraft it has ridden these last 30 years. It's not like it's going to crash straight to the ground or anything, but I don't know that there's a whole lot of people chomping at the bit to write such a thing rather than write something static and useful. Especially when you deal with the fact that even the fastest dynamic languages are still ~10x slower than compiled languages on general tasks now, and it's hard to justify going crazy with threading when a single thread in a modern statically-typed language, which may only be modestly harder to write now than with a dynamic language (if that), starts out with such a performance advantage, one that only continues to multiple if the static language also starts spawning threads.
There is Clojure, is dynamic and allows for multithreaded programming, courtesy of the JVM.
The JVM being a very dynamic system is a good target for new dynamically typed languages. But as you said, they are a hard sell today not only because of performance but more because of static typing.
And fibers are not new, but also can be hard to use and aren't super popular in general use.
So the practical answer is you probably won't use either. :)
But one way to explain the difference is that fibers are basically for cooperative/evented concurrency, while ractors are a new invention that are more like (OS native) threads but without shared memory.
Many systems using other “preferred languages” also
implement some form of GIL (e.g., Javascript’s V8 machine,
CPython); we don’t hear complaints about it, because these
languages have a rapport for heavy lifting and parallel tasks.
As a python developer i almost fell off my chair when i read this section. We don't hear complaints about the GIL in python? I love the article's tone in general but that's some heavy Gell-Mann Amnesia or filtered news sources :)"Note: Many systems using other “preferred languages” also implement some form of GIL (e.g., Javascript’s V8 machine, CPython). Python has a reputation for heavy machine learning lifting and parallel tasks. However, when it comes down to actually looking at the benchmarks, the constraints, and the underlying implementations, its quite hard to pull out any statistically significant metric or distinction between how Python and Ruby handle these tasks — what Python has in advantage over Ruby is its popularity, vast library, and very active data-science community, moreso than any benchmark. Ruby is a little faster in some things, Python is a little faster in others.
Edited: I previously had a line in here saying that we “don’t hear about” Python’s GIL, to which I received a number of messages from Python developers who assure me that it is all they talk about. Sorry Python!"
Before y'all throw tomatoes at me, allow me to make things more confusing...
The problem with elegance is that it is mostly about being clever, but cleverness is often a tradeoff for being cryptic and hard to follow for those who don't have context or understanding. Because it is an abstraction, elegance in the form of things like metaprogramming and inheritance hierarchies make the underlying ideas more difficult to understand, not less.
Many programmers, including myself at one time, confused the idea of making something abstract as making said thing simpler, but more often than not it is the underlying details that are most important to be able to read and understand as a sequence of instructions and you are abstracting at the wrong level. Languages like Ruby are already so abstract that even just a level of abstraction above what it already provides can make code murky, resulting in lots of "thinginess" but not "this does this, and then this, in order to result in this".
I'm not singling out Ruby, BTW, but the topic of the article just happens to be especially vulnerable to the issues that caused me to lose interest in it and OOP in general.
I won't go into inheritance other than that I've realized that it's a terrible idea most of the time and that a growing number of programmers agree. Over the years things like Rails have added concepts that lean more into composition, but every single Rails project I've ever worked on was a mess of countless levels of inheritance that makes it difficult to know where behavior is coming from (which is ironic in a way).
This isn't necessarily a failure of Ruby but rather its community since countless other languages implement inheritance. There's something about Ruby that encourages overuse of inheritance and so many projects suffer from the pitfalls of it whether the developers are willing to admit it or not.
Speaking of the community, I really don't understand the amount of effort they go through to avoid documenting anything. I'm not talking about the language, frameworks, or libraries, since they generally provide great API documentation, but if you are reading Ruby code you are far less likely to find inline documentation or meaningful readme files than in other languages. Strangely, after having used the Ruby language for 8+ years, colleagues still discourage me from adding comments here or there to provide context around why something works the way it does. In contrast, it's rare that I'm ever told not to add inline documentation to JavaScript code by other JavaScript devs. It does happen, but I've really only had that experience one time in my career.
Ruby developers buy this line that "Ruby is so expressive that you don't need to write documentation, and the tests will describe anything else." That sounds wonderful but, frankly, I'm not a computer. I don't live and breathe code, and I'm not interested in modeling MRI in my head so I can interpret Ruby code. Even if I did, I doubt it could actually communicate contextual ideas in a meaningful way because that's not what computer languages are designed for, because computers don't understand anything. When you throw in metaprogramming and other dangerous ideas, the "documentation" you are writing effectively turns into a Choose Your Own Adventure book where every page has a 6 line paragraph that rarely finishes a full thought and makes you jump to random pages in the book.
I used to not trust the notion that Ruby is slow, and I still think that perspective is overstated, but not in the way a lot of people think. The vast majority of Ruby projects I've worked were much slower than they needed to be and, again, it usually came down to the community not being interested in how to write code in a performant way and understand which parts of the interpreter and standard library are faster or slower. The barrier of entry to Ruby is so low that a big chunk of brand new code is written by junior developers, which is insane but also extremely economical. My first Ruby job paid me $40k (in SoCal of all places!) and although I did achieve some pretty interesting things, I also wrote a lot of crappy code that the businesses didn't care was bad. And they won't ever care until they can't hire anyone to fix the mistakes that were also piled on top of my own, in which case it will all be rewritten with PHP and React. It's astounding just how slow a Rails project can get, not just when it serves HTTP requests but in just starting up the server. JavaScript and Node has its definite faults when it comes to over-reliance on 3rd party packages, but the Ruby world too has a problem with over-reliance on packages; it's just fewer people care because Ruby's package management has advantages over the wild west of NPM. It seems that with every `rails-` gem that gets installed, the speed of everything decreases exponentially and no one bothers keeping track of it. Rather than waste time working towards 100% code-coverage, track performance* and you will save countless dollars in human hours. Ruby seems like a great way for small companies to whip out something great, but has anyone actually figured out whether less time, money, and energy is expended on Ruby projects than on alternatives? Ruby sells itself as being good for productivity, but if it's more often slower than projects that use other languages and frameworks (which is true in my experience), then it's not living up to the promise that people are still buying into.
Ruby has an awesome syntax, thus I wish there was a radical shift in Ruby, perhaps a fork, where the great parts of the language that make it especially readable and expressive could be kept while metaprogramming be made highly limited and inheritance impossible. Basically, it should get rid of the things that drag it down in terms of performance and readability and focus on what other languages managed to do right.
EDIT: Other thoughts...
Oh, and it should be made possible to declare and import modules without automagically bringing them into the global scope unless you `require` them under your own module scope. That way programmers can just write functions and not feel the need to add them under a named module or class scope.
Speaking of, the distinction of a method, proc, and lambda should go away and just be replaced with functions that have a binding. They add nothing but confusion, and I doubt most Ruby programmers even know the difference or why it is that they call everything "methods" while other languages have "functions" that can both be methods and assigned to local variables. And yeah, you can kind of do that with procs, but procs really suck.
Implicit returns are also... yeah, kinda not worth it and makes things less clear IMO. Again, I actually like a lot about Ruby, but I've never gotten used to implicit returns. I really don't think it adds anything useful to the language and just makes things more confusing for people coming from other languages.
Uh, the features you want to be kept and those you want to be limited are the same features (or, at least, the latter are a key component of the former), so that’s not going to happen.
Metaprogramming and inheritance are (key to, not all of) the features that make Ruby especially readable and expressive.
What makes Ruby expressive to me is that it relies more heavily on English language than other languages that make heavy use of symbols and overly concise naming conventions. The symbols it does use (and I don't mean the Symbol class) get out of the way when you don't need them, meaning you can often get away not using curlies or parens, which may result in code that is easier for the human eye to parse (w/ syntax highlighting) and makes it simple to write DSL-like code and APIs without going down the full metaprogramming rabbit hole. Languages that are more C-derived don't have such affordances. Conveniences for creating ranges, calculating time, having array/enumerable operators, etc., are all highly beneficial. Ruby stands out in those ways without things like metaprogramming. It's the parts that get out of the way that are great, whilst metaprogramming can easily end up interfering with anyone who didn't write it. There are certain exceptions, but they are usually well tested libraries that get a lot of time and attention. The metaprogrammed DSL one guy wrote, who left the company before you showed up, can have flaws that take hours to flesh out.