-
Rails is absolutely fantastic for projects below 10,000 lines with 1 or 2 contributors, especially if you want a classic forms-based UI. And you can get a huge amount done under those constraints in Rails.
But as of couple of years ago, Rails came with a number of drawbacks:
1. There was no really viable system of static typing that a significant number of people were enthusiastic about. See https://www.reddit.com/r/ruby/comments/105sdax/whats_the_lat... for a discussion.
2. The lack of static typing meant far less IDE support. Fewer documentation tooltips, less autocompletion, etc.
3. I used to do a lot of Rails consulting. And whenever I had to drop into a codebase with more than 50,000 lines or 5 active developers, it was generally a painful slog. Too many weird Rails plugins that stopped being maintained, too much magic, too many nasty surprises while refactoring.
Basically, smaller Rails projects were an absolute delight. Larger Rails projects, though, tended to feel more like a swamp. Tools like https://activeadmin.info/ could tip the balance where applicable.
I still think that small Rails projects are fantastic, and I don't think anything since has remotely matched Rails' productivity within that niche. There's just too much mature tooling, and much of it works together seamlessly. But not too many projects want classic multi-page apps right now, and small projects often grow up to be big projects.
-
CodeRabbit
CodeRabbit: AI Code Reviews for Developers. Revolutionize your code reviews with AI. CodeRabbit offers PR summaries, code walkthroughs, 1-click suggestions, and AST-based analysis. Boost productivity and code quality across all major languages with each PR.
-
-
NXUI
Discontinued NXUI (nex-ui), next-gen UI - Create minimal Avalonia applications using C# 10 and .NET 8
-
-
A internal network boundary is probably worth it for heavy jobs, since you usually don't want it to interfere with serving web requests (no matter the tech).
You probably already know what I would say to each of those examples.
> Rails timing out after 30s while allocating 500MB of memory (mostly) in ActiveRecord to compute 5MB of JSON to return to an API caller.
I can make a JS or Go program perform the same way. In fact the exact same thing happened in my shop with Go/Gorm. The key question is: how do you compute the 5mb of JSON? The devil is in those details. We changed the way we computed ours, and the issue was gone.
> 90% of request latency of ~10s spent waiting for downstream services to respond to requests. Most of these could be fired off concurrently (ie `Promise.all` in node). 9s/10s this Rails worker is sitting around doing nothing and eating up ~300MB of memory.
This sounds broken. Why is the worker doing nothing for 9 out of 10s? But like I said earlier, there are a bunch of ways to use HTTP1.1 pipelining to run them concurrently. (https://github.com/excon/excon and https://github.com/HoneyryderChuck/httpx support it, but you can also do that with Net::HTTP I believe) And you can still start threads, which are still concurrent while blocking on IO.
> trying to extract out Authorization to a centralized service (so that other extracted services don't have to call into the monolith in order to make authorization decisions) is a major pain as the monolith now has to make calls out to the centralized auth system to in order to make authz decisions.
This seems unrelated to Rails. Not sure why monolith can't continue handling authorization.
-
A internal network boundary is probably worth it for heavy jobs, since you usually don't want it to interfere with serving web requests (no matter the tech).
You probably already know what I would say to each of those examples.
> Rails timing out after 30s while allocating 500MB of memory (mostly) in ActiveRecord to compute 5MB of JSON to return to an API caller.
I can make a JS or Go program perform the same way. In fact the exact same thing happened in my shop with Go/Gorm. The key question is: how do you compute the 5mb of JSON? The devil is in those details. We changed the way we computed ours, and the issue was gone.
> 90% of request latency of ~10s spent waiting for downstream services to respond to requests. Most of these could be fired off concurrently (ie `Promise.all` in node). 9s/10s this Rails worker is sitting around doing nothing and eating up ~300MB of memory.
This sounds broken. Why is the worker doing nothing for 9 out of 10s? But like I said earlier, there are a bunch of ways to use HTTP1.1 pipelining to run them concurrently. (https://github.com/excon/excon and https://github.com/HoneyryderChuck/httpx support it, but you can also do that with Net::HTTP I believe) And you can still start threads, which are still concurrent while blocking on IO.
> trying to extract out Authorization to a centralized service (so that other extracted services don't have to call into the monolith in order to make authorization decisions) is a major pain as the monolith now has to make calls out to the centralized auth system to in order to make authz decisions.
This seems unrelated to Rails. Not sure why monolith can't continue handling authorization.
-
"I can make a JS or Go program perform the same way. In fact the exact same thing happened in my shop with Go/Gorm. The key question is: how do you compute the 5mb of JSON? The devil is in those details. We changed the way we computed ours, and the issue was gone."
The problem is ActiveRecord in my case. The data layout is not great (lots of joins through relationships, I thik 12 tables or so). What do you use (except raw sql) in Ruby if you cannot use ActiveRecord? I actually reimplemented the same API endpoint in Go using https://github.com/go-jet/jet and measured 10MB of allocations and essentially zero overhead over queries itself, a 50x speedup.
-
SaaSHub
SaaSHub - Software Alternatives and Reviews. SaaSHub helps you find the best software and product alternatives
-
"The amount of APIs in the world that require concurrency within a single request scope to meet latency needs approaches zero."
I've never worked on any non-Rails API where this was true. The Ruby community keeps telling me this, but in languages where concurrency is well supported, it gets used everywhere to a great extent. I obviously don't have hard data to support this, but your claim seems pretty far fetched to be honest.
"In practice, you don’t make that db call until the auth request is done and the user is verified"
Of course you optimistically do any idempotent DB operations while waiting for auth to succeed if you care about latency.
"you don’t make the outgoing api call until you already have the results of the db call, because you need your data to form the outgoing request"
These dependencies of course exist, but so do parts of the graph where they do not. You might want to make 2 or 5 outgoing calls based on your DB query and have to wait for 2 out of these 5 to make another DB query. This is so common that there are libraries like https://github.com/uber-go/cff to explicitly model those dependency graphs, visualize them and resolve them at runtime.