Lemmy is an open-source, federated link aggregator and discussion platform similar to Reddit, Lobste.rs, or Hacker News. The software stack used in Lemmy includes Rust with Actix and Diesel for the backend, and TypeScript with Inferno for the frontend.
The developers chose Rust for its performance, safety, and concurrency features, which help in building a reliable and efficient backend. Actix is a popular Rust web framework that provides a lightweight and fast foundation for the server-side application. Diesel is a Rust ORM (Object-Relational Mapping) and query builder that simplifies database interactions. On the frontend, TypeScript offers better type safety and tooling compared to plain JavaScript, while Inferno is a fast and lightweight React-like library for building user interfaces. These technologies were chosen to create a performant, reliable, and easily deployable platform.
I read someone saying that the lack of contributors was due to the software stack being unconventional and takes people a while to get used to it. So I was curious to know what other people would have used.
I could be mistaken, but I believe these sites are normally “using rails” (as in they shell out to other languages, Go has become common, for performance critical end points or portions of those end points that need to work a bit harder). I also suspect they’ve got some pretty heavy modifications.
I haven’t worked with Ruby since… 2.5(?). That’s very fair about the JIT improvements.
The Ruby language itself has fibers, but last I knew Rails was not making use of fibers for ActiveRecord. The problem is effectively you’re in an interpreted language (bad enough) without real parallelism (worse). So, when any IO occurs one of your non-parallel threads is blocked. Ruby jumps around to process the different threads, but this creates a context switching problem and you lose a lot of performance to the preemptive multitasking’s context switches (and resources are hard locked up by the IO).
With a coroutines or fibers based approach (cooperative multitasking) you don’t need multiple threads, the “callbacks” just fire into the appropriate place when IO is fulfilled (what NodeJS is famous for). You can get way better throughput and it becomes trivial to execute queries that aren’t co-dependent in parallel (so your page that depends on 10 different queries gets resolved much faster, and your app can work on other pages while that data is being resolved in the background).
It’s not a silver bullet, you can run into issues if you have CPU bound tasks on the event loop thread (and thus create latency issues as things just aren’t getting finished). However, in a well designed web app that shouldn’t happen anyways (this stuff should be in background job queues, or at the very least running in separate threads – in Ruby that likely means processes unless the threading has significantly improved).
That’s good to hear.
RBS is definitely newer (I stopped working with Ruby around 2018). Looks like Sobet was just getting started. Also good to hear.
I’ve changed my stance over the years from “it’s a matter of preference” to “it’s objectively bad.” The lack of any type structure leads to a significantly larger requirement in unit testing. Unit testing can be good, don’t get me wrong, but in our org it had gotten to the point we were writing unit tests that effectively tested “did the code change” … and I don’t think that’s a good workflow. You’re lacking a compiler to do type checks for you (when you refactor something) so you have to write all the code to catch things the type system would’ve taken care of for you.
Put another way, I don’t consider a unit test for a function as trivial as:
return a.y + 1
To be superior to the type system telling me
y
no longer exists. However, ify
no longer exist, I still want to know before a customer bumps into the inevitable crash.The problem you run into here is a lack of mature libraries to leverage if you want to do anything non-trivial. e.g., our application needed to read from spreadsheets. The Ruby libraries either A) didn’t have support for common formats like
xls
or B) would load the entire spread sheet into memory represented as Ruby objects (you can predict how well that performed :) ).Without potentially breaking a NDA, that made the effort to get efficient spread sheet processing much more challenging than it would’ve been if we’d picked a language with more reach; this is just the main example I think of, there were others.
I think we eventually started using something akin to that. However, I’m a big advocate for making the wrong thing look wrong/complicated, and Rails very often makes the wrong thing look simple. Note that gems like this don’t really solve the problem they just inform you when you’ve made the mistake, or alternatively forcing your app into hammering your database even in situations when it doesn’t need to (which can drive you back to my earlier points about the lack of cooperative mulitasking to deal with the IO bottlenecks).
I agree, but there is something to be said for hiring people that are extremely knowledgeable in the framework to help highlight solutions (like those you’ve mentioned here) vs “you’re a great C++ dev, now go do Ruby!” It’s not a deal breaker, but when you hire folks like that the time to get them up to speed is going to be far higher, and they’re going to make mistakes that in a sense “aren’t at their experience level” (particularly with the former point about making the wrong thing look wrong considered).
I’ll concede my initial reply/stance was a bit bold, and you’ve definitely highlighted serious improvements in the ecosystem I’ve either overlooked or forgotten about… Perhaps I should’ve said was terrible; I’m not rooting for Ruby to fail, but I did have a bad experience.
I can’t speak to what they’re doing internally, but I would agree this is a safe assumption. I think the point though is that Rails, with some help of external services depending on use case, can certainly be scaled out to a level that is far above the average web service making it more than capable of handling 95% of use cases you could throw at it. In the case of Shopify, they’ve traditionally been great about contributing optimizations back to Rails so their performance optimizations are shared with the larger ecosystem (and has the benefit of keeping them closer to upstream).
To your point, Ruby recently introduced Ractors which are true parallelism. They’re new enough that their use isn’t widespread yet, but I’ve played around with them and it’s definitely neat to have real parallelism in Ruby now. And for web services, this would depend too on the web server. For example, using Puma or Unicorn will have multiple worker processes so there is some parallelism between requests regardless.
Yeah, I get that. For what it’s worth, I really can’t think of a situation where there hasn’t been some library written in Ruby for something that I needed shy of extremely esoteric stuff that I likely would have needed to write myself if working in another language anyway. But that’s going to be highly dependent on a case-by-case basis. For what it’s worth, I make it a point to use as few third-party libraries as I can unless they’re highly popular. It’s a problem in all languages that random person’s pet project library, while highly useful, becomes abandonware far too often.
You might be referring to the Bullet gem which is just a notification that there’s an N+1 and where to find it so it may be fixed. However, the JIT Preloader gem actually does automatically solve the problem of N+1s in nearly all cases (see the README for details if you’re curious). It’s the closest to a silver bullet solution for the N+1 problem as I’ve seen and I now give almost zero thought to N+1s anymore. I know the devs were wanting to get it merged into Rails to solve this problem for everyone, but I don’t know what happened to that effort.
Right, I wouldn’t portend that anyone can make an easy switch from, say, embedded systems to web development in a few weeks or even a few months. I meant more like if you’re competent web developer the core concepts of building web backends/frontends don’t vary all that much between frameworks. At the end of the day, the underlying concepts deal with HTTP and HTML/JS/CSS so if you have a solid understanding of the base concepts and system design for the backend it shouldn’t be much trouble to switch the framework sitting on top of those, especially if you have a team around you that is effective at code reviews, answering questions, and generally investing in new employees. Like you said, switching from something totally unrelated is a different situation though.
Thanks for a thoughtful reply, by the way. :) I really shy away from getting into pointless internet fanboy debates over which tool/language/framework is “best” but always enjoy when there’s thought out reasoning behind points.