The REPL: lets you have unified development and operation. Makes it easy to record and review methodology when taking administrative action.
Testing: both made more necessary by the dynamic typing of Ruby, but more importantly, without disturbing the program under test much you can inject any input, any fault, nearly anywhere. In this way you can accrete invariants guarding against a large class of bugs encountered in production into the test suite, a form of retained learning.
The libraries: Sequel, Roda, Rodauth, part of the Jeremy Evans universe. They're very good by any standard, not even as confined to the Ruby world. The maintenance level on those is something I aspire to, but have never been able to duplicate. I did copy the 100% line (and later, branch) coverage project practice from this, though, in Citus Cloud and later projects, and it seems like a good thing, though not for the reasons people might anticipate.
An anti-requirement: control planes often have fairly lax performance needs relative to operation value. A few crucial loops, like monitoring, may indicate something with more efficient machine code. But mostly it's a fancy SSH client with quite a few tests, and some carefully designed concurrency patterns. Of the above factors, I'd say the REPL is the most dispositive for why I tolerate the performance and parallelism ramifications of Ruby, though, they all add something to my decision to use it for this.
This design theory is the fourth in a progression for me, though there are other clades by now. Heroku Postgres was the first, Citus Cloud was the second, Crunchy Bridge is the third, this is the fourth.
I'd have picked elixir... Much better concurrency and distribution support. SSH is built into the (erlang) standard lib, really fantastic support for templating via EEx (if you're using libvirt, e.g.). Easy to deploy as an on-metal or containerized platform using releases. Ruby deployment dependency management can be tricky unless you have a way to isolate from the host (containers e.g.) but that's not the best for being a hypervisor.
They're not a hypervisor, though. They provide a control plane that uses one via ssh. Choosing to deploy outside containers at this point in time should in my opinion be a last resort almost irrespective of what you're deploying; if the surface you interact with the host is so complex you can't grant access with granularity to a container without it becoming problematic (or at the very least via a tool providing similar levels of isolation using namespaces), chances are you have a bigger problem.
I've considered at various points (but not with deep seriousness I admit), typescript, julia, scala, swift. You might notice all of these have a REPL in common. Elixir might be a reasonable alternative, but not one I exercised here. In areas I wanted to take risk, I had some other stuff going on.
uh just fyi: Elixir's repl is the most powerful of all of them, as it gives you incredible introspection. With care you can attach a repl to a remote running program (in prod, if you take appropriate precautions) and be able to diagnose the running state of your program quite effectively -- like you can examine the tail call state of any given process.
> With care you can attach a repl to a remote running program
That's good, but it's also table stakes. Drb (in the standard library) lets you remote access any Ruby object, so if you want a remote REPL, it's is simply a case of injecting a Drb server. Hence e.g. pry-remote which remotes the Pry REPL, but you can also then remote any already existing object in any running application.
I mean the equivalent is also possible out of the box Erlang VM. Without having to install sidecar servers or monkey patching objects with potentially spooky action at a distance.
Erlang, from day 1, was designed to be logged in to remotely. No other system is remotely close to the sophistication that Erlang (and as a result elixir) has in this regard
1. it's not compiled and evaluated at runtime, which make it harder to test. Pesky undefined variables in edge case code..
2. deployment is tough when it's a ton of small files. Most things compile to a single executable binary
3. it's not concurrent or parallel easily. You're not going to easily use all your cores and also develop sane software.
Ruby parallelism with request-based server apps probably works the same as other managed languages with single threaded runtimes (eg v8, Python) - you run multiple server processes. Requests are independent so its's an "embarassingly parallel" problem without need for communication between threads of execution.
If you deploy in a container (and if not, why?), the number of files is irrelevant.
Difficulties using all your cores with Ruby was a problem when I first started using it 18 years ago. It hasn't really been a problem for things like web serving for the last ~15 or so.
"All right" is a stretch. Shopify employs a lot of amazing compiler engineers to constantly evolve the ruby compiler/VM to squeeze as much performance as possible without affecting the regular engineers, something most startups cannot do.
Being engaged in cost optimization engineering after you've "made it" doesn't mean failure, quite the opposite. And the parts before that are much harder bottlenecks, where other aspects of tech choices are rightfully weighed more.
Ruby web applications have a history of absurdly high resource usage and failure stories compared with something like Go. If the selling point of this is for individuals and small businesses to run this instead of AWS/GCP, it may not be any cheaper once you start running this control plane.
AWS/GCP is frequently 2x-3x as expensive as doing it yourself, depending on your usage patterns as some things are vastly more overpriced than others (e.g. egress), sometimes a bit lower, but if your control plane is more than a rounding error in your overall cost you're doing something very wrong.