Makes me miss Ruby. Been in node typescript recently. Everything is a callback returning a promise in some weird resolution chain, mapped and conditional types, having to define schemas for everything and getting yelled at by lsp all day... Oh then you gotta write react components and worry about rerenders and undefined behavior caused by impurity in state, npm, arcane .json configs
Versus active record, mvc, yaml configs, bundler, beautiful syntax, robust and trivially extendable stdlib, amazing native debugging and cli docs out of the box, everything out of the box if you're using Rails
I do not understand why it becomes increasingly irrelevant, especially in web development. I kinda get scripting--bash and python tend to run everywhere
Lack of static types is one of the main reasons. Trying to decipher a complex ruby on rails codebase is unnecessarily difficult compared typescript. The tooling is also shit unless you use Ruby Mine.
An absolute shame given how good the functionality is baked into RoR.
byroot sets a great example sharing his code optimization expertise. His blog has many great improvements like this. A 7x improvement in Dir.join and similar calls?! Thank you, byroot!
Nothing ground breaking we simply deploy Buildkite agents on EC2 nodes.
As mentioned in the post, the only thing really limiting CI parallelism is the ratio of "setup time" vs "test time". If your setup time is too long, you hit diminishing returns fast.
> More importantly, on CI systems it’s relatively common to check out code using git, and git doesn’t care about mtime
git doesn't care about mtime, but git maintains trees whose hash changes if any constituent part of the tree changes. It'd seem tempting to check for a .git and if present use the git tree to determine whether to invalidate the cache.
Aside from the oddness of making this cache git aware, with the new implementation I suspect querying git to revalidate the cache would take longer than just rebuilding it.
Looking up the hash of a tree in git is few enough operations that I would be very surprised if that is true for all but the smallest caches. If you were to shell out to the git binary, maybe.
What happened to Ruby? It was very successful at some point.
Maybe kids started using JS exclusively. But what happened to older developers? Did they move over?
Rails seemed to enable very fast prototyping and iteration. Isn't it still the case?
I see PHP usage going down, but PHP doesn't seem to have any advantages over JS, .NET, Python or Go. While Ruby coupled with Rails promised easy and rapid development.
Of course, Ruby might not be best suited for large code bases or microservices but probably 90% of the Internet are small to medium web sites.
> Ruby might not be best suited for large code bases
Ruby/Rails powers some of the largest platforms on the planet - Shopify, GitHub, GitLab. Both have had something of a resurgence lately, too, with Ruby 4 and Rails 8 shipping recently, and people rediscovering that Rails is excellent for vibe coding.
I've been a Ruby developer for 10+ years and have never struggled to find work, and the communities feel very active and growing - so I'm honestly not sure what you mean by "what happened to Ruby". If you don't actively follow or participate in the community, I can imagine you wouldn't hear much about it day to day.
I don't pay attention to the JS world these days - what happened to JavaScript?
Nothing has happened to Ruby, it's an excellent choice for software. We're basically at a point where majority of languages and stacks are on par with each other (unless you have niche requirements)
PHP + Laravel give the same easy and rapid development as Ruby + Rails. Plus PHP reads as a programming language. Ruby reads as it was written by somebody with heavy brain damage struggling to put words into sentences.
> But what happened to older developers? Did they move over?
I'm working on a project with a Rails backend and a Vue frontend.
I've been working on a JS backend and frontend project in the early 2010s but I think that it was the only project with a JS backend. There are plenty of projects with server generated HTML. In my case the backends were Ruby, Python, Elixir, Java, PHP.
People should. I seriously miss using it at my day job. It's not for code where type systems make things a lot more stable, but it's great for scripting and quick things. Also ORMs in ruby are truly nice, and I haven't found anything as good anywhere else.
Frameworks and packages, sure. I’m not sure I would agree with APIs.
ActiveAdmin is best in class, Rails is fantastic; but there’s a lot of insanity in the API for a language that “gets out of the way” and “just works”
Slice is my favorite example. (It’s been a bit since I’ve used it)
[0].slice(0, 100) == [0]
[].slice(0, 100) == …
exception? Or nil? Why does it equal []?
For a “give me an array back that starts from a given, arbitrary index, and auto-handle truncation” not having that behavior continues to confuse me from an intuitive perspective. Yes, I understand the source of it, but why?
Because [] is an array with nothing in it, and [0] is an array with something in it.
So saying “give me the array containing the first 100 elements of this array with one element” would obviously give you the array with one element back.
Saying “give me the array containing the first 100 elements of this array with zero elements” would follow that it just gives the empty array back.
On top of that, because ruby is historically duck-typed, having something always return an array or an error makes sense, why return nil when there’s a logical explanation for defined behavior? Ditto for throwing an error.
Yeah, returning an empty array is pretty much exactly what I would expect given the first example. It would be a lot weirder to me if you were allowed to give an end index past the last element only if the array happened to be non-empty.
So, there is a behavior difference between "array a little too short" and "array slightly more too short" that creates unexpected behavior.
That's not a big surprise in a tiny example like this; but if you expand this out into a larger code base, where you're just being an array and you want the 100 through 110th values for whatever reason - say it's a csv. Suddenly you're having to consider both the nil case and the empty array case; but then why are they different?
Interesting! From playing around with it, seems like if the start index is exactly the same as the length, it returns empty array, but if it's further than that it returns nil. That's certainly not something I would have been able to predict, so I'd also be curious if anyone happens to know the explanation for it. My instinct is that it does seem like the type of edge case that might come up with a way to implement it tersely, but that's not a particularly good reason to leak that in the form of user-facing behavior, so hopefully there's a better explanation.
Some additional things I discovered when trying to figure out why it might work like that:
* the behavior also seems consistent whether using `array.slice(a, b)` or `array[a..b]`
* `array[array.length]` and `array[array.length + 1]` both return nil
the docs say... if index is out of range return nil. the edge case is that if you specify the exact end index of the array and want a slice of that index to 100 it will return an empty array. if you go out of bounds it informs you that you are out of bounds with nil. not sure it's the best api but probably is mimicking some C api somewhere as a lot of ruby does that. that said it will never error on this alone but it will almost certainly error if you chain it with something not expecting nil.
The easiest way to get around that if you are not carefully using the ranges would be to do `Array(array.slice(a, b))` as that will guarantee an array even if it's invalid. you could override slice if you really wanted to but that would be a performance penalty if you are doing it often.
looked into it more and the docs say that an index out of bounds will return nil. also says if offset == size and length >= 0 it will return an empty array.
```
If offset == self.size and size >= 0, returns a new empty array.
If size is negative, returns nil.
```
either way if you are doing stuff with arrays and not checking bounds you can throw an `Array(some_array.slice(x, x+100))` and it will always behave.
I actually think types are an anti pattern. I’ve seen more code with type escape hatches than bugs in Ruby. The truth is if you follow TDD and good coding patterns the bugs in a dynamic environment are unlikely to show up.
For pretty much everything. My terminal is in Ruby, with a Ruby font renderer, running Ruby shell, and my editor is in Ruby, my window manager, my file manager.
(Yes, I'm taking it a bit far; my prototype Ruby compiler is self-hosting finally, so I guess sometime in the next 20 years I'll end up booting into a Ruby kernel for no good reason...)
I really like Ruby. It had a formative impact on my young programmer self, particularly the culture. So much joyful whimsy.
But like... something like a font renderer in Ruby? The thing that is incredibly cache sensitive and gets run millions of times per day on a single machine? The by far slowest step of rendering any non-monospaced UI?
It doesn't typically get run millions of times per day because in most regular uses it's trivial to cache the glyphs. I use it for my terminal, and it's not in the hot path at all for rendering, as its only run the first time any glyph is rendered at a new size. If you want to add hinting and ligatures etc., it complicates the caching, but I have no interest in that for my use, and then it turns out rendering TrueType fonts is really easy:
(Note that this is a port of the C-based renderer libschrift; the Ruby version is smaller, but much less so than "usual" when converting C code - libscrift itself is very compact)
Absolutely yes, all over the place! Startups are building greenfield software with Rails as we speak. Loads of established businesses have Ruby applications that are quietly chugging along doing their jobs well. & Shopify, a company with $1.6 billion in annual revenue, uses Ruby _very_ heavily & also invests in the wider Ruby ecosystem.
Ruby is not without its drawbacks & drama, but it’s elegant in a way that few languages are to this day (how many JS programmers _actually_ grok prototype-based object-orientation?) & compared to NPM, RubyGems is (lately) unexciting in the best way.
I use Rails for many of my side projects. Because of the emphasis on convention over configuration, Rails codebases tend to be succinct with minimal boilerplate, which keeps context windows small. That in turn makes it great for agent-assisted work.
For web stuff, with server-side rendering and partials it means minimal requirement to touch the hot mess that is JavaScript, and you can build PWAs that feel native pretty easily with Hotwire.
Ruby is slow as fuck though, so there's a tradeoff there.
YJIT is amazing but for me, JRuby and TruffleRuby were the real game changers.
For anything "slow" I can put it in Sidekiq and just run the worker code with TruffleRuby.
I have high hopes for ZJIT but I think TruffleRuby is the project that proves that Ruby the language doesn't have to be slow and the project is still getting better.
If ZJIT, JRuby or TruffleRuby can get within 5-10% of speed of Go without having to rewrite code I would be very happy. I don't think TruffleRuby is far off that now.
Makes me miss Ruby. Been in node typescript recently. Everything is a callback returning a promise in some weird resolution chain, mapped and conditional types, having to define schemas for everything and getting yelled at by lsp all day... Oh then you gotta write react components and worry about rerenders and undefined behavior caused by impurity in state, npm, arcane .json configs
Versus active record, mvc, yaml configs, bundler, beautiful syntax, robust and trivially extendable stdlib, amazing native debugging and cli docs out of the box, everything out of the box if you're using Rails
I do not understand why it becomes increasingly irrelevant, especially in web development. I kinda get scripting--bash and python tend to run everywhere
Lack of static types is one of the main reasons. Trying to decipher a complex ruby on rails codebase is unnecessarily difficult compared typescript. The tooling is also shit unless you use Ruby Mine.
An absolute shame given how good the functionality is baked into RoR.
Skill issue
I think the only thing you need is grep and know what you are doing.
> getting yelled at by lsp all day
God forbid you get forced to fix bugs before they reach production.
This might shock you but vast majority of bugs are logical.
Sure but I like it when I don’t have bugs that can be detected by static analysis.
> Everything is a callback returning a promise in some weird resolution chain
Care to provide some examples of this? This hasn't been my experience, in general.
byroot sets a great example sharing his code optimization expertise. His blog has many great improvements like this. A 7x improvement in Dir.join and similar calls?! Thank you, byroot!
> Given that the Intercom monolith CI runs with 1350 parallel workers by default
Wow! I'd love to hear more about how that's achieved
Nothing ground breaking we simply deploy Buildkite agents on EC2 nodes.
As mentioned in the post, the only thing really limiting CI parallelism is the ratio of "setup time" vs "test time". If your setup time is too long, you hit diminishing returns fast.
It's because it probably uses Ruby on Rails which has to launch 1000s of os process to handle any traffic.
This makes no sense and has nothing to do with serving traffic.
More EC2 workers means more parallelism means tests map to more workers and the CI build finishes faster. It’s just CI perceived complete time.
> More importantly, on CI systems it’s relatively common to check out code using git, and git doesn’t care about mtime
git doesn't care about mtime, but git maintains trees whose hash changes if any constituent part of the tree changes. It'd seem tempting to check for a .git and if present use the git tree to determine whether to invalidate the cache.
Aside from the oddness of making this cache git aware, with the new implementation I suspect querying git to revalidate the cache would take longer than just rebuilding it.
Looking up the hash of a tree in git is few enough operations that I would be very surprised if that is true for all but the smallest caches. If you were to shell out to the git binary, maybe.
Would this be possible to mainline into ruby in some way?
From the article: "This new feature will be available in Ruby 4.1.0."
Thanks, missed that.
Wondering what else is in store for 4.1. ZJIT?
https://github.com/ruby/ruby/blob/master/NEWS.md
What happened to Ruby? It was very successful at some point.
Maybe kids started using JS exclusively. But what happened to older developers? Did they move over?
Rails seemed to enable very fast prototyping and iteration. Isn't it still the case?
I see PHP usage going down, but PHP doesn't seem to have any advantages over JS, .NET, Python or Go. While Ruby coupled with Rails promised easy and rapid development.
Of course, Ruby might not be best suited for large code bases or microservices but probably 90% of the Internet are small to medium web sites.
> Ruby might not be best suited for large code bases
Ruby/Rails powers some of the largest platforms on the planet - Shopify, GitHub, GitLab. Both have had something of a resurgence lately, too, with Ruby 4 and Rails 8 shipping recently, and people rediscovering that Rails is excellent for vibe coding.
I've been a Ruby developer for 10+ years and have never struggled to find work, and the communities feel very active and growing - so I'm honestly not sure what you mean by "what happened to Ruby". If you don't actively follow or participate in the community, I can imagine you wouldn't hear much about it day to day.
I don't pay attention to the JS world these days - what happened to JavaScript?
Ruby on Rails died because it was a resource Hog.
People moved to efficient IO requiring smaller servers.
If you make Ruby on Rails in a typed compiled language and show people how fast it is. People will switch in an eyeblink.
Nothing has happened to Ruby, it's an excellent choice for software. We're basically at a point where majority of languages and stacks are on par with each other (unless you have niche requirements)
PHP + Laravel give the same easy and rapid development as Ruby + Rails. Plus PHP reads as a programming language. Ruby reads as it was written by somebody with heavy brain damage struggling to put words into sentences.
> But what happened to older developers? Did they move over?
I'm working on a project with a Rails backend and a Vue frontend.
I've been working on a JS backend and frontend project in the early 2010s but I think that it was the only project with a JS backend. There are plenty of projects with server generated HTML. In my case the backends were Ruby, Python, Elixir, Java, PHP.
don't take this the wrong way, but -- people still use ruby?
People should. I seriously miss using it at my day job. It's not for code where type systems make things a lot more stable, but it's great for scripting and quick things. Also ORMs in ruby are truly nice, and I haven't found anything as good anywhere else.
Generally speaking Ruby has the best APIs.
Frameworks and packages, sure. I’m not sure I would agree with APIs.
ActiveAdmin is best in class, Rails is fantastic; but there’s a lot of insanity in the API for a language that “gets out of the way” and “just works”
Slice is my favorite example. (It’s been a bit since I’ve used it)
exception? Or nil? Why does it equal []?
For a “give me an array back that starts from a given, arbitrary index, and auto-handle truncation” not having that behavior continues to confuse me from an intuitive perspective. Yes, I understand the source of it, but why?
Because [] is an array with nothing in it, and [0] is an array with something in it.
So saying “give me the array containing the first 100 elements of this array with one element” would obviously give you the array with one element back.
Saying “give me the array containing the first 100 elements of this array with zero elements” would follow that it just gives the empty array back.
On top of that, because ruby is historically duck-typed, having something always return an array or an error makes sense, why return nil when there’s a logical explanation for defined behavior? Ditto for throwing an error.
Seems thoughtfully intuitive to me.
Yeah, returning an empty array is pretty much exactly what I would expect given the first example. It would be a lot weirder to me if you were allowed to give an end index past the last element only if the array happened to be non-empty.
Especially because in ruby
[0, nil, nil, nil, …x100, nil] is the same as [0] in terms of access.
In both cases, trying to access the 100th element (e.g. [0][100]) will give nil.
Sorry, I mis-spoke earlier, this is what I should have shared:
^-- *THIS* either returns nil or throws an exception.
Edit: Longer example:
Yields:
So, there is a behavior difference between "array a little too short" and "array slightly more too short" that creates unexpected behavior.
That's not a big surprise in a tiny example like this; but if you expand this out into a larger code base, where you're just being an array and you want the 100 through 110th values for whatever reason - say it's a csv. Suddenly you're having to consider both the nil case and the empty array case; but then why are they different?
Interesting! From playing around with it, seems like if the start index is exactly the same as the length, it returns empty array, but if it's further than that it returns nil. That's certainly not something I would have been able to predict, so I'd also be curious if anyone happens to know the explanation for it. My instinct is that it does seem like the type of edge case that might come up with a way to implement it tersely, but that's not a particularly good reason to leak that in the form of user-facing behavior, so hopefully there's a better explanation.
Some additional things I discovered when trying to figure out why it might work like that:
the docs say... if index is out of range return nil. the edge case is that if you specify the exact end index of the array and want a slice of that index to 100 it will return an empty array. if you go out of bounds it informs you that you are out of bounds with nil. not sure it's the best api but probably is mimicking some C api somewhere as a lot of ruby does that. that said it will never error on this alone but it will almost certainly error if you chain it with something not expecting nil.
The easiest way to get around that if you are not carefully using the ranges would be to do `Array(array.slice(a, b))` as that will guarantee an array even if it's invalid. you could override slice if you really wanted to but that would be a performance penalty if you are doing it often.
looked into it more and the docs say that an index out of bounds will return nil. also says if offset == size and length >= 0 it will return an empty array.
``` If offset == self.size and size >= 0, returns a new empty array.
If size is negative, returns nil. ```
either way if you are doing stuff with arrays and not checking bounds you can throw an `Array(some_array.slice(x, x+100))` and it will always behave.
because it's meant to be a more functional language. if slicing an array out of bounds threw an error it would be java.
[].slice(0, 100).each do |x| puts x end
that shouldn't be an error and it seems to be the principle of least surprise imo.
Sorry, I mis-spoke earlier, this is what I should have shared:
^-- *THIS* either returns nil or throws an exception.
( I made the other comment like this longer, please use that one for context )
I actually think types are an anti pattern. I’ve seen more code with type escape hatches than bugs in Ruby. The truth is if you follow TDD and good coding patterns the bugs in a dynamic environment are unlikely to show up.
Ruby on Rails is the GOAT. Nothing comes close in joy and productivity, even in 2026.
For pretty much everything. My terminal is in Ruby, with a Ruby font renderer, running Ruby shell, and my editor is in Ruby, my window manager, my file manager.
(Yes, I'm taking it a bit far; my prototype Ruby compiler is self-hosting finally, so I guess sometime in the next 20 years I'll end up booting into a Ruby kernel for no good reason...)
I really like Ruby. It had a formative impact on my young programmer self, particularly the culture. So much joyful whimsy.
But like... something like a font renderer in Ruby? The thing that is incredibly cache sensitive and gets run millions of times per day on a single machine? The by far slowest step of rendering any non-monospaced UI?
The Earth is weeping my brother.
It doesn't typically get run millions of times per day because in most regular uses it's trivial to cache the glyphs. I use it for my terminal, and it's not in the hot path at all for rendering, as its only run the first time any glyph is rendered at a new size. If you want to add hinting and ligatures etc., it complicates the caching, but I have no interest in that for my use, and then it turns out rendering TrueType fonts is really easy:
https://github.com/vidarh/skrift
(Note that this is a port of the C-based renderer libschrift; the Ruby version is smaller, but much less so than "usual" when converting C code - libscrift itself is very compact)
ruby and rails is the only stuff that keep me doing web development.
when I touch js, and python... I prefer ONLY AI agentic style of working.
What’s the right way to take this?
Absolutely yes, all over the place! Startups are building greenfield software with Rails as we speak. Loads of established businesses have Ruby applications that are quietly chugging along doing their jobs well. & Shopify, a company with $1.6 billion in annual revenue, uses Ruby _very_ heavily & also invests in the wider Ruby ecosystem.
Ruby is not without its drawbacks & drama, but it’s elegant in a way that few languages are to this day (how many JS programmers _actually_ grok prototype-based object-orientation?) & compared to NPM, RubyGems is (lately) unexciting in the best way.
It's my daily language and I don't even use rails nowadays.
Same. I've used Rails a few times, but something like 95% of my Ruby use over the last 21 years has been non-Rails.
I use Rails for many of my side projects. Because of the emphasis on convention over configuration, Rails codebases tend to be succinct with minimal boilerplate, which keeps context windows small. That in turn makes it great for agent-assisted work.
For web stuff, with server-side rendering and partials it means minimal requirement to touch the hot mess that is JavaScript, and you can build PWAs that feel native pretty easily with Hotwire.
Ruby is slow as fuck though, so there's a tradeoff there.
Not really slow since YJIT, I think 3.1?
Even with yjit it's still more than twice as slow as even Go, to say nothing of C# AOT, which depending on the benchmarks is like 4x as fast.
YJIT is amazing but for me, JRuby and TruffleRuby were the real game changers.
For anything "slow" I can put it in Sidekiq and just run the worker code with TruffleRuby.
I have high hopes for ZJIT but I think TruffleRuby is the project that proves that Ruby the language doesn't have to be slow and the project is still getting better.
If ZJIT, JRuby or TruffleRuby can get within 5-10% of speed of Go without having to rewrite code I would be very happy. I don't think TruffleRuby is far off that now.
Ah yeah I'm only vaguely familiar with Go. Didn't realize the speed differential was this drastic.
Ruby is amazing. The software industry has suffered because people think Ruby isn’t the right choice and try to static type all things.