What's New in Ruby on Rails 8
blog.appsignal.com
blog.appsignal.com
- The pickaxe book, Programming Ruby, has a new edition this year covering Ruby 3.3
- The Rails Way is being updated for Rails 8 and is available in pre-release, and will have two companion books
- A new title, Rails Scales, is being published by PragProg and is available in pre-release now
- YJIT has made Ruby fast. Like, _FAST_
- Rails has a bunch of new features that cover the "missing middle" levels of success
- Ruby has a bunch of new and new-ish features like Data (immutable Struct), pattern matching, Fibers and Ractors, and more.
I had largely moved on from ruby into the world of front-end applications by 2013, but now I'm excited about the prospects of diving back in.
Seriously, other projects can use its success as a reference for implementation.
And I say this as a front end dev.
Part of it was due to the rise of SPA and Rails difficulty working with those (webpacker, anyone?), part due to poor perception of Rails 4, part due to newer options such as Elixir/Phoenix or Golang rising in popularity for backend work, part due to many of the leaders such as Yehuda Katz moving on.
Also watching from afar on HN, it seems like Rails 7 release was perceived as a sort of comeback, with quite a few articles posted in recent years praising the framework for a return to relevance.
I suspect Django and Laravel have taken a chunk of the market as like for like replacements.
Elixir/Phoenix were embraced with excitement due to Jose’s connection to the community and the ruby like syntax.
Go is good, but again I only tried it long ago and can't comment it for what it is today. I loved Ruby but I find it hard to go back to it after my experience with Elixir and Typescript. I was hoping for Crystal to go to great lengths but it doesn't seem to be the case at all.
Of course it was tongue in cheek, if the candidate is amazing yes they're a hire.
But it spoke to a reputation that Rails devs had seemingly received. I think because prior to JS/Node, it was Rails that offered newbies the fastest path into web dev.
I don't believe this is the reason for any sort of exodus, but the negative perception may be partly a reason for devs choosing other frameworks.
Then I pray with all my heart that GitLab moves to it, because that's one of the major complaints I hear about folks who use their web interface. Even while I was visiting the site to look up whether their main repo had a .devcontainer folder in it, I could just watch all the stupid ajax-y shit spin endlessly in what is conceptually a static table (sure, it changes per commit, but they're not a stock ticker platform)
OT1H, I know, I know, "MRs welcome" but OTOH as a ruby outsider getting the dev environment up for contributing to them has been a lifelong struggle. Now that Rubymine has support for .devcontainer maybe it'll work better
YJIT does make Ruby fast, but that doesn't mean it makes _Rails_ fast. (yet) At least dont expect multiple times improvements.
Hopefully in the future.
There is also plenty of perfectly snappy Rails applications out there. People need to stop blaming the framework...
Regrettably, since GH doesn't develop in the open, it's hard to have an apples-to-apples between the two in order to know if GH has some really, really tuned RoR setup, or it literally is just a matter of "don't be cute" when sprinkling every JS trope known to man upon your web ui and if GL one day cared about their page load times, they, too, could be quick without needing a RoR nor RVM upgrade
It's inspiring other's in the community to think of interesting applications, like using the HTML slot API to stream responses to HTML without JS. https://x.com/joeldrapper/status/1841984952407110037
I know other frameworks have had asynchronous IO support forever, but it's finally coming to Ruby that seems like it will stick around and be well supported.
Only action cable still doesn't fully support falcon using http 2. But that's coming soon as well.
More concurrency is not always ideal, especially if you’re not in an environment that guarantees you won’t have negative impacts or runaways process (BEAM languages, etc).
Rails projects are typically so hardwired to use a background queue like Sidekiq that it becomes very natural to delegate most use cases to the queue.
Is this due to increased memory usage? Does the same apply to Sidekiq if it was powered by Fibers?
If you're making API calls out to external systems, you can use all of the concurrency that you want because the outside systems are doing all of the work.
If you're making queries to your database, depending on the efficiency of the query you could stress your database without any real benefit to improve the overall response time.
If you're doing memory intensive work on the same system then it can create a potential issue for the server and garbage collection.
If you're doing CPU intensive work, you risk starving other concurrent processes from using the CPU (infinite loops, etc).
Something like the BEAM is setup for this. Each process has it's own HEAP that's immediately reclaimed when the process ends without a global garbage collector. The BEAM scheduler actively intervenes switch which process is executing after a certain amount of CPU time, meaning that an infinite loop or other intensive process wouldn't negatively impact anything else...only itself. It's one of the reasons it typically doesn't perform as well in a straight line benchmark too.
Even on the BEAM you still have to be cautious of stressing the DB, but really you have to worry about that no matter what type of system you're on.
Or are you saying that processing lots of requests/tasks in Rails while waiting for the database would quickly eat up all the CPU? It seems like a good thing - "resource utilization" = servers should do things whenever possible rather than just waiting. Although now that I think about it you'd only want maximum resource utilization if your database is on a separate server.
If you have inefficient queries, N+1 problems, competing locks, full table scans, temp tables being generated, etc then more concurrency will amplify the problem. Thats all I meant.
Rails has been really slow to pick-up async/http-2. They don't know it yet, but Falcon and all async libraries Samuel is working on will probably be a huge them 1-2 years out when more people find out it means less infra has to be deployed to production environments. Right now folks are happy to deploy without Redis™ with the Solid stack, but a lot of that won't be needed if proper async support is baked into Rails.
There's been a lot of Fiber features being committed into the Ruby language that I barely understand, but have improved all of these async libraries over the past few years. That's finally starting to bear some fruit for people like myself who don't really understand all of those details, but understand the benefits.
It will happen, but these things tend to play out more slowly in Ruby, which is a feature or a bug depending on how you look at it.
This is so condescending... We perfectly know about the pros and cons of the fiber scheduler.
It's a very useful stack, but people, and you in particular, really need to stop selling it like it's the best thing since sliced bread. Async isn't a good fit for everything, and it's certainly not a good fit for the overwhelming majority of Rails applications.
"it's certainly not a good fit for the overwhelming majority of Rails applications".
In my experience, most web applications are terminating HTTP connections from clients, then reaching out over a network to database servers, etc. to do work. This is very much IO-bound, so I'm not sure how this wouldn't be a good fit for most Rails applications.
Bringing http/2 all the way to the Rails process doesn't bring anything to the table. You're much better to terminate http2 or 3 with SSL at the LB.
> terminating HTTP connections from clients, then reaching out over a network to database servers, etc. to do work. This is very much IO-bound
It absolutely isn't unless your data access is really messed up (badly indexed queries or tons of N+1).
Even if you are just serializing the data you got from the DB down into JSON with little to no transformation, you'll likely end up spending more than 50% doing CPU work.
Look at all the reports of YJIT speeding up Rails applications by 15 to 30%. If Rails apps were truly IO bound like people claim, YJIT would have nothing to speedup.
Even if your app is 90% IO, you can slap Puma with 10 thread and will already suffer from contention. Async make sense when you'd need more than a dozen threads or so does. Before that it doesn't make a substantial difference. Like it would be great to use for Action Cable, but that's it.
Most rails applications are deployed using a multi-threaded application server such as Puma, a thread processes a single request and when it encounters IO (or calls out to a C function) the thread gives up its hold of the GVL and another thread can run. You can use 100% of your resources this way without the added complexity of parallelism within a single request.
Anyway, I'd be careful about over-promoting Falcon/Async, even as one of the relatively few people that's running in a large-ish production app. In my case, I'm doing a LOT of hanging waiting on API responses from weather data sources, and I want to transform the JSON and do some data point conversion in real time. So even in my case, I think it's better for me to switch (see https://github.com/socketry/async-examples for me doing some experiments) because I'm often waiting on API responses (in real time) that can take 1-2 seconds (!) but then I'm still doing some heavy CPU work reading and writing JSON blobs. I saw a pretty good speedup with YJIT, so I think I'm not entirely IO bound, if that makes sense.
I think it's great to have more options for those of us that love Ruby, so we don't need to switch to Node.js or something else for this kind of work. But I think it's likely that existing Rails apps (and typical CRUD Rails apps etc) will probably want to stick with a thread-based model. We'll see how it all shakes out over the next couple years, and I think you're right that it's exciting stuff. Between all the work on YJIT etc, and the possibilities for Node.js-type or Go type use-cases being possible/reasonable with Fibers, it's a great time for Ruby!
But also there is very little benefit to it for the vast majority of Rails applications out there.
Unless you are doing micro-services or something like that, your typical Rails application isn't IO heavy enough to run more than 2 perhaps 3 threads before contending.
So the overwhelming majority of Rails applications wouldn't see any benefit from being served via falcon, quite the opposite.
Async is great to enable some use cases where people would have to reach to Node before, but Rails will never be a good solution for that, if you want to do some sort of light proxy or websocket notification thing, Rails isn't a good solution, you want something much lighter.
If you don't, you'll notice that your Action Cable latency will be all over the place when a transaction request comes in.
It's acceptable for "hobby" use, but if you try to provide a good user experience with reasonable latency, you can't just collocate Action Cable and the rest of Rails in a single process.
Do you have any tutorial somewhere on how to use Falcon for this? I am getting some strange errors that would probably be covered in a basic tutorial already.
- Ruby and Rails now has all of the things mentioned above and more. I do have concerns that Rails will evolve in directions where bundled frontends have less official support, with the continued centralization of 37signals/DHH [0] and their controversial removal of Typescript from Turbo [1] (and bundling in general for Hey), but it's such a large community that there will be continued momentum in all directions.
- Python used to be the choice if you expected to do both data processing/machine learning/NLP and web backends in a centralized codebase with a single language. And Django is still a world-class solution there, with gevent + asyncio + forthcoming developments on GIL-less Python all contributing towards making Django a highly performant and parallel framework. That said, with much of an app's data processing complexity often best offloaded towards LLM-powered solutions that have dedicated APIs, and both Ruby [2] and Node having bindings to https://pola.rs/ as an alternative to Pandas, it's no longer the only solution.
- And on the JS end, frameworks that enable full-stack batteries-included admin-out-of-the-box development like https://redwoodjs.com/ and https://www.prisma.io/nextjs + e.g. https://next-admin.js.org/ continue to evolve. Nowadays, if you're building a complex web application from scratch, Prisma provides all the escape hatches you'd need, so that you can build entirely in JS/TS and have the facilities you'd expect from Rails or Django.
I'm really excited that all three communities are continuing to push the boundaries of what's possible; it's amazing to see.
[0] https://news.ycombinator.com/item?id=30600746 [1] https://news.ycombinator.com/item?id=37405565 [2] https://github.com/ankane/ruby-polars
Curious, I tried to look for some benchmarks, which still seems to show Node often being 3-10x faster
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Sadly, a lot of people still live in the Ruby 1.X era and thinks its slow.
Anyway, there have been updated versions of the books and content online if people are interested.
Most changes have been in libraries and interpreter / VM implementation.
Updating your knowledge from Ruby 1.8 (mid 2000s) to 3.x (current) takes little effort.
But yes, sparse API documents were always a problem because a big chunk of the community was Japanese.
https://web.archive.org/web/20230608024407/https://www.ruby-...
The thing discouraged me to go further was that many sections has work in progress label. I thought I might be looking at the beta docs, but no. I checked the same page down to multiple major version and the work in progress was label was still there.
Coming from Django/Laravel world, incomplete docs discouraged me to try rails.
We should see a huge reduction by the time the project wraps, but because Rails itself is constantly evolving, so to is the documentation, so you will continue to see them from time to time. Consider it a sign of growth, not stagnation.
Btw I am contributing myself this year to the book ecosystem by writing Kamal Manual and Test Driving Rails.
I am sure more pure Ruby books will happen too by someone.
I'd say's it's completely the opposite.
Yes, microservices might've been the trend in late 2010s, but after everyone got burned by it with unnecessary complexity (unless you're at "Google" scale), people just went back by being productive building modular "monolithic" architectures, and using "microservices" only when absolutely necessary.
If you're looking for the "mindset" of microservices, where you store the data separately and impotently, then I believe Rails discourages that mindset.
Honest question, who ever seriously proposed microservices to improve performance? It doesn't take a lot of thought to figure out that microservices have overhead that will always put it in a disadvantage over a monolith in this regard.
The only "scale" that makes sense wrt microservices is that of the scale of teams, and manage-ability of the scale of features and codebase. They are primarily a solution to "too many people working in too many poorly bounded domains". But as a solution to performance problems? Is it really proposed as that?
Then for some reason some junior engineers thought that you could make everything an api call and you can make services in the most optimal language and glue them all together to have functional “scalable” apps.
And thus the horrors of being a web dev in 2016+ began.
Of course it didn’t help SPAs were encouraging backends to be decoupled from front ends and completely hidden in their implementation so the fact that “it was now possible” enticed backend devs to experiment with multiple api services.
I have, certainly, replaced some endpoints in Rails apps with lambda's, rust, or even standalone sinatra services for performance reasons.
For example an endpoint that generated "default stable avatar pngs" for new users: Ruby just isn't cut for image generation and manipulation. Rewriting that in a stack that performed x100 in this use-case (we picked rust) took a lot of heat off the cluster of servers.
Or moving the oauth and registration to a separate rails app that served these pages - the only endpoints that did HTML. Allowing the "main" Rails app to remain leaner by not loading all of the templating, and other HTML middleware in memory when it would never be used.
In that sense, I guess, monolyths can have a performance disadvantage: they require the entire app to load stuff for that one endpoint or feature even if 99% of the requests and users never use that.
Like the "PDF generation for reports" we once had, that was rarely used but still loaded in every running thread that would never handle anything related to reports or PDFs. Extracting that to a separate "PDF report generation worker" freed GBs of memory on almost all servers.
The PDF use case also sounds very promising low hanging fruit
That was actually excacly our angle of attack: see the routes, modules or jobs that were putting the most pressure on the servers.
Then copy the entire app over to separate servers, connected to the same db cluster. Have an app router direct everything except, say /reports/ to the old servers, and /reports/ itself to the copies on new servers.
Did the old servers reduce significant in load? Rip out the part there. Better? Now rewrite, cleanup, isolate or extract the, e.g. /reports/ part on the servers. Better?
Then, finally, disconnect the service from the shared DB (microservices sharing a DB is the worst idea ever.) and have it communicate either via a message bus, via REST calls or not communicate at all.
Now, take your auth logic and put it on a 3rd party, rewriting all of your auth to do so.
Now, make your database shared across multiple distribution platforms and 12 services (aws, cloud, heroku, tableau).
When one of your 15 services goes offline for temporary maintenance, for some reason your entire website goes down.
The 17th service you created has an ip address switch and is missing and the response to all urls is the default apache gateway page.
The 24th service upgraded from Node 12 and is now broken, while the 26th service built in Go doesn't compile on the specific linux variant of one of your devs.
Before you know it, you're just doing maintenance work because something is broken and it isn't your code, it's some random downtime or brittleness that is inherent in microservice architecture.
These problems are independent of "microservices" vs "monolith". They are independent of "using a framework" vs "no framework". They are independent of programming-language or hosting infra.
Managing complexity, in itself, is a daunting task. It's hard in a monolith, it's hard in microservices. Building a tangled big ball of spaghetti is rather common in e.g. Rails - it takes a lot of experience, discipline and dedication to avoid it.
Languages (type systems, checkers, primitives), frameworks, hosting infra, design patterns, architectures, all of these are tools to help manage the complexity. But it still starts with a dedication to manage it today, and still be able to do so in a decade.
Microservices don't inherently descend into an unmanageable tangle of tightly coupled, poorly bounded "services". Just as a monolith doesn't inherently descend into an unmanageable tangle of tightly coupled, poorly bounded "modules".
Scaling services independently is usually a recipe for outages where something is at the wrong scale. Sometimes you want to separate workloads that don't fit the request response model well because they take too long or use too much CPU or RAM, but you don't need micro services to get that benefit.
I don't think anyone was claiming it would lower latency for typical requests except maybe indirectly through materializing views in event-driven architecture.
I think the steel man has been about scaling teams, but the discourse was not limited to that.
You still can run into situation where adding a network call is small overhead over the optimization available where it has its own datababase running on its own VM where you can add more resources just for that specific thing.
Maybe you can rewrite that part in a language that fits use case better for only that service.
By "plain monolith" I meant just any kind of monolith.
This is basically what's accomplished by publishing a Thrift/Proto/OpenAPI IDL from a collection of packages comprising a network service. The key insight is that the serialization and networking parts of this are superfluous, what you actually wanted was the visibility rules.
I suppose places like Spotify or Github may have good practices around working on large monoliths but I would think that takes a lot of effort to get right and may have trade-offs.
It was always more of a team organization solution than a system architectural solution. Leaning into it too much on the latter created a lot of complications for the former.
That said, I think interesting possibilities exist where the two approaches are blended. I work for a cloud [1] that supports a concept of "services" grouped within an app each of those services is a web server that can be configured to sleep [2] under specific conditions -- effectively serverless but without the loss of shared memory and all the efficiencies of running a more traditional web server.
The grouping of services also provides a way to spin off parts of an application while keeping it within the same deployment process.
The number of hi-tech companies that are in the middle-to-large scale have increased significantly from the first wave of Rails era.
Majority of hi-tech companies with listed stock have complexity more than "monolithic" architecture.
Sadly, if a company doesn't grow, they will get eaten by their competitors.
Isn't that kinda circular? Generally speaking, companies only list their stock when they grow large. The vast majority of web dev is not happening in those kinds of companies.
Then they go off and build complicated monorepos that take 6 months to learn before you can effectively contribute.
All paradigms have trade offs. Engineering is about understanding those trade offs and using them where appropriate. Unfortunately too many people just jump on the "k8s is complicated" bandwagon (because it challenges most of the assumptions they've previously held in software development) and entirely write-off microservices without taking the time to learn what problems microservices solve.
Debugging is different and some people find that harder because it's not what they are used to. That does not mean it is actually more complicated.
Nobody says you need http, sqs, grpc, and queue (message broker) all in the same system. Nor does anyone say you need to use 4 languages for these things, nor does anyone say you need to only enable observability when there's issues.
Bunch of handwavy "it's complicated" nonsense basically - demonstrating yet again most people don't really understand microservices.
A microservice can be as simple as studying it's inputs and outputs and nothing more. All of which can be observed via tracing, logging or whatever you prefer, either on a sampled-basis, only on errors, or all the time.
Microservices make you rethink almost all of your software engineering assumptions - and some people are just not in the right headspace to make the jump. The biggest change is viewing/treating your codebases like cattle more than pets. Automate everything, fail quickly, stop caring about crashes, keep services as small and focused as possible, stop worrying about multiple supported releases, etc.
It is very different, but that doesn't mean it needs to be complex.
That doesn't mean microservices are the solution to every problem. It does mean, however, it's a solution to more problems than some people are willing to admit and/or take the time to understand.
> A microservice can be as simple as studying it's inputs and outputs and nothing more. All of which can be observed via tracing, logging or whatever you prefer, either on a sampled-basis, only on errors, or all the time.
In a statically-typed monolith, function calls are typed, I can jump to the declaration site with a mouse click and if I pass the wrong type, the code won't compile. That's way easier than "you can observe the inputs and outputs" (and hope that your observations generalise sufficiently). And yes, there are solutions for this (contract tests, Avro schemas, etc.), but they are inherently more complicated (maybe the most interesting idea I've seen in this space is Unison[1], but that's not gonna gain a lot of traction anytime soon, I predict).
Of course, microservices have valid use cases, but they come at a significant cost. I don't understand how one can argue that that cost doesn't exist.
[0]: https://en.m.wikipedia.org/wiki/Fallacies_of_distributed_com...
I give it to you that, we're it not for microservices, I wouldn't need as much of http, sqs, etc, as well requiring making sense out of all of it via tracing/metrics/logging...
I've worked enough with microservices and heard enough managers preaching the same "cattle not pets", "small focused services", and similar, enough to know that high turnover or several rounds of layoffs, unreasonably high cloud bills and clients frustrated with high latency, often caused by several services, is the inevitable outcome.
I'm of the opinion that this was always bad advice for most people most of the time. Achieving any real infrastructure cost savings this was is difficult, its easy for delivery speeds to suffer, and the debugging experience is terrible.
a senior engineer will try to keep the monolith for only as possible and only then explore a new tiny service.
People really underestimate how good modern RDBMS are, especially when they have their query cache tuned properly, there's no reason to be slower than Memcache or Redis.
Node at work, Rails on the side. Something like that. Feels like a lot to master, but not sure.
Node makes things like OAuth trivial with the mix of Express and Passport, something that took me two weeks in Rails. But man does Rails make Sequelize look childish by comparison.
It is much easier to switch between them than I first expected. I can quite easily use the idiomatic patterns in either framework. But there are obviously very large differences in the two stacks. The most important that come to mind:
- Rails is much more "batteries included". No need to decide what ORM, queue, mailer, remote storage, etc you want to use for the new project. You just use the built in solution. And you place the files in a pre-defined location. That makes it really easy to onboard new people, because they are familiar with the file structure. On the other hand, for Node projects, you can mix and match your components, and use simpler structures for side projects, and more complex structures (including tools like Nx) for more complicated projects.
- The Rails ORM is much more powerful than the Node counterparts. I think it helps, that the ORM is built into Rails, so everyone contributes to the same product. For Node, it feels as if there are many ORMs that are 70% done, but don't cover important cases. One of them is fast but a bit buggy, one is slow but has the ability to deal with schema migrations, one is old and works well but does not support TypeScript.
- Documentation of Node frameworks, for example NestJS, seems to be updated quicker than the Rails counterparts. For Rails, especially when Turbo was released, there was a kind of vacuum regarding the documentation. The docs for Turbo did explain the basics, but it was very very difficult to get it working with the official docs. Once blog posts started to pop up, it became much easier. Projects like Nest seem to have much more detailed documentation for their components.
All in all, I do prefer Rails tbh. The DRY principle that the framework follows makes sure that you don't have to edit 8 files when adding a new database column. This makes development much faster and in my opinion less error prone.
One important aspect of this that I think isn't well-understood in the JS ecosystem is that existence of standard "blessed" solutions means that the rest of the ecosystem can be built around them, especially tooling. With JS and Node, every time there's a new fancy framework, you have to wait for IDEs to catch up to give you any kind of convenience above and beyond just typing code. I was just looking at test frameworks the other day, and the state of IDE integration (with stuff like e.g. Test Explorer in VSCode) is appalling even for well-established ones like Mocha. Ruby guys don't have to rewrite everything every two years, so their tools tend to be much more polished in that regard.
I've used Ruby long enough to have some gripes about it, but I've worked in a half-dozen languages as well, and I have yet to meet an object model that I like better. That combined with the standard library, and the affordances for functional data pipelining, are what keeps Ruby among my favorites.
(Tuples are coming to JS and will solve this particular issue, but it's embarrassing to have to deal with such stuff in 2024. Neither Python nor Ruby ever had this problem.)
Don't sell yourself short my friend.
People who know a fraction of what you know call themselves professionals and ask for raises.
Chin up!
It only took me a couple of months to realized what Ruby on Rails offers & the vibrant ecosystem it has. I quickly pivoted back to it and never looked back. I'm very happy with that decision.
Take that plunge and you will not regret it later.
No pipelines, no minification, no nonsense, just serving js and css directly.
Hey.com works like this, and they reported nothing but improvement.
Bundling is also something that we no longer need to do since the introduction of HTTP2. I would even consider it an anti-pattern these days, since it means you're sending the client much more data than they actually need.
Tree shaking is definitely very important for something like tailwind though.
rails new -d postgresql --css tailwindThis does not become easier with monkey patching and open classes + lots of meta programming.
I started with Rails during version 1.x and went up to 2.x with it, good fundamentals of Rails have not changed and are the same (inspired others to adopt such as ORM/convention over configuration etc) but lately, I ended up with Go instead for being at least some type system no matter how crude and having a single binary in the end to deploy.
Never have to worry about pip/gem/npm crashing on me. Mostly.
The idea of Rails 8 is to move away from trend following and try to reject some of the ideas that have become popular in the industry (like being afraid to touch a Linux server or implementing your own auth). Really though provoking stuff.
Goes on to show how for many applications overly priced platforms as a service aren’t really needed but incidental complexity masquerading as essential complexity is peddled as a way to make money.
htmx, Rails, and Laravel all point to better ways, and people are starting to be receptive.
Back when Rails burst into the scene, he picked the EJB mountain to battle and positioned Rails vs EJB for as long as he could.
This is another battle in the Infrastructure world that he picked.
The timing was perfect too: Rails grew during economy crisis 2006-2009 (layoff, EJB complexity, web-app complexity, the rise of single-developer entrepreneur). Right now we're sort of in similar situation too: layoffs, infrastructure complexity, BigTech culture, FAANG-leetcode grind.
Tech is like a cycle :)
I think rappers invented starting a beef to shift records.
just my two cents, the web development has become akin to the fashion industry.
We've seen a steady stream of frameworks emerge, from traditional PHP and JavaScript to JavaScript-based development like ReactJS, AngularJS, and now WebAssembly while tried-and-true methods still work.
all the new frameworks bring benefits – but also introduce problems a few years later. The trends drive the IT industry, fueling a cycle of book sales, training camps, and consulting services that corporations buy into every few years.
React is 11 years old, AngularJS is 14 years ago, let it go already, gramps.
It's their own E2E solution for deploying and running Docker apps in production. Solves SSL, proxy, zero downtime deploys, etc.
They changed the implementation of an optional library. The interface of the library didn‘t change at all. Only how it was implemented.
Also, how is he able to use private fields in the js example? Is he using any polyfilling?
I'm still working with RoR.
It's still an incredibly quick, powerful, flexible, versatile framework. I'm able to build pretty large and complex apps all by myself, quite quickly, without fuss.
I'm looking forward to the deployment improvements since that was one of the shortcomings that was still around. Kamal v1 didn't really do it for me (currently using Dokku instead). Maybe Kamal 2 is it.
[0] https://nts.strzibny.name/multiple-apps-single-server-kamal-...
I supposed it'd be nice to have one less thing to manage, but I'm wondering if there are any obvious gotchas to moving these features over to sqlite and postgresql.
The main argument for caching in the DB (the slight increase in latency going from in-memory->DB-in-memroy is more than countered by the DB's cheapness of cache space allowing you to have tons more cache) is one of those brilliant ideas that I would like to try at some point.
Solid job - i just am 100% happy with Sidekiq at this point, I don't understand why I'd switch and introduce potential instability/issues.
That said you can probably get away with caching way more with solid_cache and it's highly dependent on what your use cases are.
Also a thing to note that your DB backing solid_cache might also be using RAM efficiently, giving you a lot of benefits that Redis did.
For new projects I'll be using solid_cache first and adding Redis only on an as-need basis. The cost of Redis is orders of magnitude higher than a DB backed cache.
But, as your app grows, you can swap things out. Redis or Elasticsearch are solid choices if you need them. DHH mentioned that as well, at scale, everyone does things differently anyway. But you do have the option to keep it simple, by default.
For me personally, Rails 8 is great. My new project only need Postgres and that's it. I don't need Redis, multiple gems for background jobs or cache anymore. Able to avoid the chaotic JS ecosystem for frontend. Hopefully it will be easy to setup Hotwire Native too. It really streamlined things, and letting me focus on building features instead.
That said, for my production apps in existing companies, I’m sticking with what’s already working: Sidekiq, Redis, Elasticsearch. If it ain’t broke, why fix it? Will probably revisit this decision again in the future. Too early to tell for now.
Solid cache is perfect for my use case since page caches doesn't change as often, so taking a smaller memory footprint on the server farm is a win.
My take is to measure your cache hit percentage. This will allow anyone to understand their cache eviction rates. If you have high eviction rates maybe using a btree is not the way to go and redis is probably better
I once worked on the integration of an existing Rails app into an existing C# webapp (both living side-by-side, kind of a split-application thing, but seamless to the end user). It was almost hilarious how many LOC the C# teams (!) were having to write for every LOC I (alone) was writing in Rails. Looking through their diffs, it seemed like they were reinventing fire compared to me writing `render :success if SomeModel.update(model_params)`.
Rails simply does more for you out the box with just a few macros.
https://github.com/mbleigh/acts-as-taggable-on
It's not only that RoR comes with a complete toolset -- it allows you to create your own libraries that extend the capabilities of the framework while keeping the developer experience terse enough through metaprogramming (compare the sample code in the README with the file acts-as-taggable-on/lib/acts-as-taggable-on/taggable.rb, where you can see how the library is opening the classes in runtime through the class_eval technique.
I'm sure something similar can be achieved in C# but not so sure about the elegance of the result.
Statically typed languages usually have other, better ways, to associate metadata with the types, if necessary. Or avoid having such a requirement at all.
> For instance, in a social network, a user might have tags that are called skills, interests, sports, and more. There is no real way to differentiate between tags and so an implementation of this type is not possible with acts as taggable on steroids.
Isn't that's just having a property/field on a struct/class/type/object that contains the list of tags? Active Records seem to be effectively EF Core's tracked entities but with seemingly more "magic" under the hood (something that ORMs are often criticized for, including catching undeserved strays by EFC).
In general, I remain very negative towards Ruby and Ruby on Rails because most feedback that I hear leaves an impression of "so it has some of the advantages similar to top modern web frameworks in C# and Java/Kotlin but with a huge bag of breakage risk with codebase growth, a need to extensively cover code with tests and static analysis to cover for mistakes that are impossible in statically typed languages, and one tenth of performance on a sunny day".
Now, I don't think it's a bad choice in a vacuum, but it's important to consider the overall experience and practices used in a language A vs B, and whether either is used correctly.
The tagging problem in particular isn't such a hard problem that you' should need to pull in an extra dependency just for that. It's basically just a nested set of hash maps, the "hardest" part about it is the persistence - and I do believe it's worth spending maybe 2 hours on a proper DB schema in exchange for having code you own and understand.
There are other libraries in the Ruby ecosystem that take different approaches (dry.rb for example, which IMHO, strikes a better balance between the expressivity of Ruby and solid design), but they're not all that popular.
I have never used the library, but it seems you get a lot more with just 2-3 lines of configuration (e.g. for a tag context named „interests“):
- The ability to find the n most or least used tags with a single method call - The ability to track which user tagged a record - The ability to find records with the same tags - Calculation of tag statistics for a tag cloud
Now, all that would certainly be possible with EF. But many libraries for Rails give developers a very simple way to configure a feature, and deliver very expressive methods to use the feature. Which is an important property imo, since it often makes obvious what the code does, even for newcomers.
This is probably an effect from Ruby itself, where the standard library is quite expansive and has many often-used patterns built in. For example, calculating an arrays maximum value is just
arr = [1,2,3,4]
arr.max
Meanwhile, in JS: arr = [1,2,3,4]
let max = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) max = arr[i];
}
And to address the README of the above library: I think it is a bit confusing because it starts with a comparison with another library, expecting readers to already know how tagging worked there. let arr = [1, 2, 3, 4];
let max = arr.reduce((a, b) => Math.max(a, b));
Or C# and Rust (which will be >100x faster at finding max value[0][1]): var arr = new[] { 1, 2, 3, 4 };
var max = arr.Max();
let arr = [1, 2, 3, 5];
let max = arr.iter().max();
Ruby does nail the minimalism in this code golfing example, but it does not offers uniquely high productivity to the end user, which is a frequently brought up point in defense of interpreted languages whenever their shortcomings are mentioned. Lacking static typing, Ruby users have to resort on e.g. Sorbet, which is a worse experience and numerous comments on HN seem to provide negative feedback on it.I do actually hate to mention performance every time, but it's difficult to not do so when apples-to-apples comparison can be made. Compiled statically typed languages with GC offer similar or better (because the code is verified by compiler, not Sorbet) productivity without any of the drawbacks that come with Ruby.
This is to illustrate the point about the languages that do come with rich standard library, that also happen to go to great lengths at ensuring that shortest way to express something is also the fastest whenever possible.
[0]: https://github.com/dotnet/runtime/blob/main/src/libraries/Sy...
[1]: https://godbolt.org/z/srfWE9qcE
(the >100x figure is not an exaggeration)
[1,2,3,4].reduce((a,b) => Math.max(a,b),100)
// or
Math.max(...[1,2,3,4])What's the chance that this is the exact set of features that you're gonna need and not a slightly different set of features? E.g. maybe your users are part of teams and you need to know which tags were set by a given team? Or maybe your tags are hierarchical? Will the library be flexible enough to accommodate that?
It just seems that this library makes a lot of assumptions about the business logic of your application - but that can change at any point and then you're possibly stuck with a library that you have to weirdly workaround, or rip out entirely.
I would understand that tradeoff if the library solved an actually complex use case, but every competent developer should know how to implement these use cases from scratch in a relatively short amount of time, and while it undoubtedly takes longer than just adding 2-3 lines of config, what you get in return is code that you own and understand and can modify in whatever way you want.
More like 50-100x.
In regular "line of business" code this difference might be a bit difficult to see in between the calls waiting for DB and third-party dependencies, so I went with more conservative numbers. On the more complex code this difference will be totally observable.
For this set of teams, at least, their code wasn’t very performant because it took 3 months to get from Jira ticket to production. They were always getting themselves tied into knots about how to handle rollbacks across their nine microservices required to change a birthday.
Meanwhile, I’m in my corner with my nice little monolith that could read data from their database easier than they could read it.
I personally got tired of the monkey patching, ActiveRecord's proclivity towards n+1 queries, and complete lack of types / static analysis, among other things. Having to keep a mental model of the types of the entire project, and having to write tests around types takes up a significant amount of my headspace, energy, and time. The older a project gets, the more of a headache these things become. There is an element of "just write better code" to avoid these pitfalls, and that works to an extent, but the older I get the more I realize that the tooling does play a large role in the outcome.
A lot of issues in and with Rails code leans heavily on this idea. It often comes in other flavors like "You can write rails apps that perform really well" or "Its perfectly doable to write a clean easy maintainable codebase in rails".
And is apparent in the "Provide sharp knives" doctrine as "And by capable, I mean knowledgeable enough to know when and how, accordingly to their context, they should use the different and sometimes dangerous tools in the drawers."
I really dislike this approach. I don't want something that is full of foot-guns and therefore requires me to recognise them when I lack all information to do so.
I've written terrible Rails apps, exactly because it allowed me to so, and I didn't know they would become terrible, until they were. I now recognise the (design) patterns that are foot-guns, I now know the tell-tales that show me where the sharp knives are dangerously placed. But then I get dropped into a Rails codebase that has seen 20+ developers coming and going over a decade, as the 21st developer, and I see it filled with these footguns and dangerously wielded "sharp knives". Often there's a lot of history apparent from these previous devs who saw this too and fixed some of it. But the overall state is still abysmal.
Sure, it's all these devs that made it abysmal. But the ecosystem, the framework and the language allowed, and sometimes even encouraged them to make it that.
It's true that monkey patching is a problem. I hear you. It's more and more problem of the past though. People realized it's too sharp of a knife and stopped doing that.
EDIT: also cocoapods
At work I have been using Ruby for longer than rails has existed. In those days there were lots of neat uses for Ruby (one of the most interesting being at NASA), but none of us knew if it would ever catch on. We didn't care -- Ruby made programming _fun_, and we were happy to use it even if no one else ever did.
It was partly the language that made Ruby fun but also the community. And it's out of this community that projects like chef, puppet, shoes, etc. were born. Matz has called Ruby human- oriented programming, and I can think of like that is human- oriented than community.
1. React burst on the scene in 2014
2. the hyperscale FANG companies were dominating the architecture meta with microservices, tooling etc, which worked for them at 500+ engineers, but made no sense for smaller companies.
3. there was a growing perception that "Rails doesn't scale" as selection bias kicked in - companies that successfully used rails to grow their companies, then were big enough to justify migrating off to microservices, or whatever.
4. Basecamp got caught up in the DEI battles and got a ton of bad press at the height of it.
5. Ruby was legitimately seen as slow.
The big companies that stuck with Rails (GH, Shopify, Gitlab, etc, etc) did a ton of work to fix Ruby perf, and it shows. Shopify in particular deserves an enormous amount of credit for keeping Ruby and Rails going. Their continued existence proves that Rails does, in fact, scale.
Also the meta - tech-architecture and otherwise - seems to be turning back to DHH's favor, make of that what you will.
The number of hi-tech companies that grew from 37signals size to Uber size have also increased due to various reasons: SaaS becoming more and more accepted, WallStreet loves SaaS, and in general just more investment money in the market.
- While most 2nd or 3rd tier tech companies don't need Google scale infrastructure, SOA in Java/C# and then Go is incredibly prevalent. Many teams never had a reason to even look at RoR and its considerably worse language and runtime.
- Good ideas from RoR were copied by pretty much every ecosystem; again, most people never wanted Ruby in the first place.
- Plenty of small web shops simply stuck to PHP.
Maybe you don't hear the constant clickity-clack of endless change as a result. Also true that Ruby's most popular in writing web apps and services, where it and Rails continue to shine.
webdev FWIW is a ginormous part of this little internet thing. You know, those Web-based applications and services that run the entire global economy and human society, such as it is.
People adopted Ruby because of Rails.
Another more adopted Ruby software are probably fluentD and maybe Jekyll. The rest kind of comes and goes.
Majority sys-admin toolings have always been dominated by Python and with k8s hype, some of the toolings have been geared towards k8s automation written in Golang.
In general, sys-admin toolings are a mixed bunch: perl, shell, python, ruby
Both languages emerged in the early to mid nineties (together with Python and a few other things). But Rails was much later than that. Now the demographic of the typical Ruby/Rails developer that got into Ruby about 20 years ago straight out of college is a forty something. Still relevant but a clear minority relative to the mass of developers that came after that grew up on things like Go, Rust, Node.js, etc. And of course Python made a comeback because of AI. So, you now see a lot of relatively young people doing a lot of stuff in Python that wouldn't typically be exposed to any Ruby.
I always looked at python and ruby as similarly capable scripting languages. With python a bit more oriented towards people who were simply looking to get shit done perhaps and ruby more geared towards computer science types obsessing about things like meta programming. A lot of that crowd moved on to other languages.
Ruby so far hasn't had a comeback movement like Python. Lots of people still use it of course but it's not an obvious choice to people that haven't used it before. All the AI stuff sort of flew by it. People do a lot of stuff client side these days which makes view oriented frameworks like Rails less relevant. Though with the server side rendering come back there is still some relevance of course. But that's more of a performance optimization that came out of the node.js community. And of course Rails inspired a lot of other frameworks for different languages so its unique selling point kind of went away.
The other day, I was exploring Next.js and the number of features, possibilities were astonishing to me as someone that's pretty anti React. I do Svelte[kit] but it isn't there at all where NextJS is especially when paired with Drizzle and auth.js topped with Shadcn/Material UI/Ant Design, a skilled craftsman can crank out apps as fast as it would be for a Rails developer if not any faster.
Unfortunately, the cognitive load that comes in JS ecosystem is mainly at three levels:
- Understanding the packaging (npm, node_modues, CJS/MJS)
- Understanding the bundlers (webpack/vite/Rollup)
- Understanding the transpilers (Typescript, Babel, Less and Tailwind lately)
EDIT: Typos
https://rubyonrails.org/2024/9/27/rails-8-beta1-no-paas-requ...
https://rubyonrails.org/2024/9/27/rails-8-beta1-no-paas-requ...
But now, with the built-in authentication in Rails, they've made it almost like lego which removes the fear for me, this is such a neat and exciting feature.
Last I recall: sorbet itself is quite good once it is well established in a project, but adding type checking to a project (especially a Rails project) is a lot of work.
In his most recent interviews the philosophy can be summarised as "if you love types, good for you, but rails isn't for you"
It also help with autocomplete and linting.
I just completed a big refactoring. We have a good suite of tests. But Sorbet has provided an added layer of confidence. Especially when it comes to the treatment of null values. Sorbet will raise an error if you try to call a method on an object that may be null. So it forces you to think through: what should happen if the object is null?
So the tests combined with Sorbet typechecking made that we could just almost blindly deploy refactoring after refactoring, with only a handful of bugs for several 1000s of lines of code changed.
You can get pretty good value from things like intellisense in your editor, knowing that the constant you're referencing or method you're calling exists etc, but most libraries have no typings in sorbet-typed (beyond `T.untyped`).
The last post on Sorbet's blog was from 2022 https://sorbet.org/blog/, so I'm not sure how actively it's being developed. (compare that to typescript where you have a post every month https://devblogs.microsoft.com/typescript/)
But Rails' core value prop (doing a lot with strongly opinionated structure) is still useful to a lot of people.
My complaint was always that version upgrades in rails were too costly -- teams would spend months -- and it wasn't usually clear whether upgrading was worthwhile. Also the business aspect led to a lot of ephemeral (very hot and then soon unmaintained) patterns/libraries.
Rails 8 makes me want to look again and see how the system overall has matured.
For hard core web enthusiasts it makes sense to keep toward the cutting edge, but for boring old sites you just want have them run well and not have to worry about swapping out x for y every few months.
I look forward to swapping out sprockets but I know there'll be teething issues. Same for built in authentication, so I won't be swapping out devise any time soon, probably ever. Same with redis for solid (won't be changing any time soon).
Absolutely love the continued work on rails, but it doesn't mean I'll necessarily those new features, which is totally cool - rails doesn't force you to.
With so much mix-and-match it can get confusing though. When I see a tutorial using importmaps I get confused because I only know esbuild. Now there's propshaft. Similarly with asset pipeline, I sometimes struggle with sprockets, now there's something new to learn and all its interactions with other pieces. (mostly paths and debugging is what I have trouble with, i'm no JS wizard [or ruby wizard for that matter]).
(I just want to spend 95+% of my time making things that solve real problems, not tinkering with and finessing pieces of the framework)
In short: if your application only needs distribution-ready JS and CSS (from libraries like Bootstrap, Splide or Lightbox2) you only need importmaps-rails. If you use SASS, you need dartsass-rails. And if you need to compile TS (with esbuild or webpack) you need jsbundling-rails.
Especially the first case got a lot easier with Propshaft.
I've been looking for a framework as good as rails for typescript, but haven't found anything to that level. The closest I've seen so far have been:
* Adonis https://adonisjs.com * NestJS https://nestjs.com
Is there anything better than these for the node world?
- ORM: Like another commenter said, the ORM landscape in the node/typescript world is lacking. Every project feels like it's 70% complete and while you can find a project for each feature you want, you can't find one project with all of them. Ended up with TypeORM which has been very meh. - AuthZ: We went with casl which has a very similar feel to cancancan but lacks many of the important features for implementing more complex authz patterns like "you can edit this if you are in the group that owns this item" - Testing: Using Jest but the DSL and setup/teardown is way more complicated than rspec. - Flexibility. Patterns like single table inheritance and polymorphic relationships are poorly supported/fully lacking in typeorm and this bit us. - Learning curve: the DI system in Nest had a long learning curve, and having to sift through half a dozen or more options for each critical package (ORM, AuthN, AuthZ, etc) instead of having 1 or 2 clear choices made getting the project off the ground much much slower than anticipated. This can also just be chalked up to being green to the Node API landscape. - Adding to the learning curve, you have to be much more thoughtful about your code organization and architecture. We have an issue where we've introduced circular coupling through db-level repositories that is very hard to untangle. Rails gives you a great standard to work from that will take you a long ways
All in all it wasn't a terrible experience but the complexity of the domain lead to some challenges that we could have probably solve more elegantly and quickly in Rails. Even after a year of working the in project, I'd say my velocity for medium/simple tasks is still only 80% of what it is in Rails. Definitely lower when more complicated features are involved
- Auth, sessions, secure cookie handling, etc.
- DB interaction
- View templates, layouts, etc.
- SPA-like interactivity
- Form handling & validation
- File uploads
- Asset handling
- Emails
- Background jobs
- Security (CSRF, XSS, SQL injection, etc.)
- Logging
- Internationalization
- Testing
- Real-time functionality
With Node/Express, you're either searching for other people's libraries to use and integrate, which aren't guaranteed to synergize, or writing it yourself. You have all of that baked into Rails, tucked away neatly so you only have to write and focus on what matters. You also have an opinionated architecture so you know exactly where all the custom business logic should go.
I think if you have extremely simple requirements, or you're at a stage where you want to learn all these things by doing it yourself, it would make sense not to use Rails. But if you want to focus on delivering value ASAP, Rails (or similar frameworks like Django) are for sure the way to go.
Are people actually going to spend time to replace redis or devise?
I wish they’d add new features instead of trying to get people off successful 3rd party gems.
Also, is there a good reason to use thruster instead of caddy?
> I think there can be some overlap between rake tasks and one-off scripts and opinions may vary. In the related discussion Okura Masafumi mentions that rake tasks are one way but they can be executed multiple times, and I tend to agree, leaving rake tasks for code that is intended to be run multiple times.
> So script/ can hold one-off scripts, for example Basecamp uses script/migrate which I am guessing they use for updating data, and we had a similar folder at my previous company.
> But script/ is not only for one-off scripts, it can hold more general scripts too, for example rails generate benchmark already generates benchmarks in script/benchmarks, and at my previous company we had script/ops which had a bunch of shell scripts and ruby scripts for various ops-related things.
So really not so clear description. It caters to those who feel like they have a script that doesn't really fit in the Rake file.
Such a nicer way to build stuff.
edit: care to explain the downvotes for asking a question?
I just spent the past week working on a hackathon where I built the project using a Node.js closed-source platform that uses Fastify edge functions and Cursor's autocomplete was terrible. Much worse than what I'm used to when Cursor makes autocompletion for Ruby/Rails code. JS libraries come and go every day. This makes it difficult for Cursor team to update their LLMs to provide good autocompletion. On the other hand, Ruby and Ruby on Rails have seen very few radical syntax changes. The syntax and majority of the API has remained the same.
Ruby on Rails is so much more than just fast scaffolding. The code I've written a decade ago in Ruby on Rails, I can still pick it up today and understand the project. I can also pick up anyone else's project and figure out the intricacies of it much faster than I'd ever be able to do in say Next.js or Express project. Express feels like writing assembly (no hard feelings).
I've, unfortunately, recently started working on a project that's written in Django. I thought Django is just Ruby on Rails for Python. Boy was I wrong... But that's a story for another time.
IMO Ruby on Rails is an absolute king! However, I'm looking forward for a day when I'll be able to use one language on both frontend and backend, while having the same simplicity and functionality as Ruby on Rails. (I'm looking forward for Ruby on Rails JavaScript successor, but it's just not there yet.)
Understanding the "why" and all the default conventions really makes you get what's going on.
I guess my thought is, understanding an app is still important, and rails helps you understand easily.
Starting a new commercial project/company in 2024 in Ruby is questionable.
Do you think there is no criteria worth considering besides the size of the hiring pool? What if 2 hiring pools are sufficiently big, do you still pick the bigger one every time?
> Do you think there is no criteria worth considering besides the size of the hiring pool? What if 2 hiring pool are sufficiently big, do you still pick the bigger one every time?
It is not just a hiring pool. Look at various third-party SASS tools, they see Ruby as a second-grade language to support.