Hacker News new | past | comments | ask | show | jobs | submit login
Ruby 3.3 on Rails 1.0 (nashby.github.io)
259 points by thunderbong 8 months ago | hide | past | favorite | 72 comments



This is awesome to see that it's possible. I've always wondered about the same sort of concept, but instead it would be trying to use Crystal to run the latest Rails version. I lack the time/energy/knowledge to try it, but it would be interesting to see how close you could get to either power or port Rails to Crystal, and if it would even be worth it (in terms of speed and type safety, etc).


My understanding is that this tends to be difficult because certain really-dynamic-stuff that Ruby offers (effectively metaprogramming) are not supported by Crystal, but are heavily used by Rails.

On the other hand, Amber offers a look of what a Crystal rewrite of Rails would look like


I've done that with Liquid (https://github.com/wmoxam/liquid-crystal), and it's roughly 2X faster than Ruby 3.2 w/yjit


Admit it, you just did it because the name is perfect.


Well, you'd have to move a whole lot of code generation from load time to compile time, including the stuff based on db layout. So quite a bit of work.


I mean, by making some changes to Rails source code, it was possible to get a simple "hello world" to display.

Presumably you'd run into more bugs if you tried to do more than that.

Rails does have a test suite (hopefully Rails 1.0 had a decent one?) , I guess the interesting experiment would be how much code do you have to change to get Rails 1.0 test suite to pass on ruby 3.3.

Safe to say quite a bit more than for the hello world to run!


> it was possible to get a simple "hello world" to display.

That's not really the whole story. It was possible to generate a hello world web application server.

Would've been interesting to see if other generators, generated migrations, models, controllers, views and tests worked (scaffolding blog posts and comments).


True; the generator for a "hello world" works to generate a "hello world" app that works!

It's definitely way less than full support for a real Rails app though!


That was a fun read. You should do Rails 8.0 on Ruby 1.8 next.


In theory this is a funny idea but I think it would be a huge waste of time. Ruby syntax has changed and added so much since since 1.8 that you'd be stuck fixing thousands of syntax errors.


There is a polyfill gem that allows older Ruby versions access the newer stuff, wouldn’t that help?


Polyfill libraries can provide methods that were added in newer versions of the language (e.g. to std lib classes), but they can't provide the new syntax that has been added.

Ruby has had a lot of syntax changes over the years that are incompatible with an older interpreter. An easy example is the 1.9 shorthand hash syntax for symbol keys `{ name: "pants" }` instead of the hashrocket syntax `{ :name => "pants" }`. More recent examples would be keyword args or pattern matching.


Oof, anybody else get a wave of nostalgia seeing that start page!


Yeap straight back to late 2007 when I first created a rails project in prep for my first job out of Uni.


I remember starting on Rails 0.8, where migrations were just sql files you ran


Where we're going, we don't need any rollbacks!


Didn't most people use ant to deploy back then? I think it was a Java build tool or something? I feel like I should know this better - the maintainer of ant was actually working for us at the time, deploying early Rails apps using ant. I just remember deciding right then and there that I very much preferred writing code over troubleshooting deploys (and what we now call "devops").


I don't know about ant... we were using Capistrano back then.


I do recall when capistrano became a thing (it was called switch tower but had to change for trademark reasons if I recall). Prior to that, most folks working with Rails in production were using ant. I wouldn't be surprised if there were other ways as well back then, given it was early days.


Haha, this is really cool. And if I'm honest, very often it'll be a blog post like this that will solve some other issue I'm encountering. Thank you!


This is pretty incredible work!

(Action web service was an attempt to make Rails generate SOAP APIs as that was the style du jour at the time. Fortunately, JSON came along!)


Regression testing.

It'd be a nice regression test to have:

- Rails pegged at a particular version number and test the runtimes.

- Ruby runtime pegged at a particular version and test Rails versions


The rails team already does the latter, subject to their specified compatibility matrix. Current rails requires Ruby 3.1, so CI only runs on that version and later.

https://buildkite.com/rails/rails/builds/103981


Hold my refreshing beverage

https://github.com/lloeki/minimal-rack

{rack 1 2 3, rails 3.2-7.1, sinatra 1 2 3, grape} x {ruby 2.1-3.3} x {puma, thin, webrick, ...}


As much as people complain about Python, Ruby really went through just as much (if not more) churn over the years from backwards-incompatible changes.


Ruby has had two major backwards-incompatible change inflections: 1.9 and 3.0 (although I think that most of the removals were actually in 3.1).

The painful one is 1.8–>1.9, because that's where Ruby's string grew encoding awareness.

Going from 2.7->3.0 was not nearly as painful (mostly dealing with warnings and some code being removed from the standard library as gems that people can use if they want).

I still maintain code that is nominally 1.8 compatible and it works just fine on 3.2 (I haven't yet tested on 3.3).

What is surprising about this exercise is how little code needed to be changed on code designed for Ruby 1.8 to allow it to run on Ruby 3.3. The author doesn't say how long they spent on this, but it looks like 3–4 hours for a fairly complex piece of infrastructure code.

How much effort would be required to run Django 1.0 on Python 3.12? At least this much, I’m sure. I suspect more, but I don't know the 2->3 differences well enough to be able to estimate that.


This so much. In the first part my my work carrier I was fixing old PHP applications, nobody ever bothered to even mention software updates.

The next part, in a ruby shop, was different. We would update all the software all the time, usually pretty painless or with one or two expected issues that are easily fixed.

There was no point when it turned to old, every app was running the most recent rails and usually ruby just days after the release


Almost all of the issues mentioned in this post were during 1.8 -> 1.9 transition.

At that time odd release numbers where development versions, until this policy changed a bit later and 1.9 became an actual "GA" release.

The 1.8 -> 1.9 is really Ruby's "Python 3" moment, as it similarly improved String encoding handling, but the breakages were much more limited and it also came with pretty significant performance improvements, so the community didn't split like Python 2 and Python 3.

If you follow Ruby's development, or watch Matz talks, you see that avoiding a Python like split is really his number 1 concern.


I agree with your analysis, as a rubyist since ruby 1.8.

> but the breakages were much more limited and it also came with pretty significant performance improvements, so the community didn't split like Python 2 and Python 3.

I think perhaps the ruby community was also smaller (or at least not TOO large), had an average higher level of skill (goes with the first, usually), and was fairly committed to ruby and/or rails at the time (we loved it, there were few if any similar alternatives).

I agree that there hasn't been a similar level of backwards breaking since 1.8 => 1.9, and that if there were now it would be much more disastrous. Keyword arguments in ruby ~3.0 come closest, but still were much less disruptive than 1.8=>1.9.

I have never been a serious python user, so can't really compare to python 2=>3, but my impression is that python 2=>3 was bigger even than ruby 1.8=>1.9. *BUT* that other typical python "minor" releases may actually have fewer backwards breaking changes than typical ruby "minor" releases?


> I think perhaps the ruby community was also smaller

Back in 2008-2010 when Python 3 was released, Python really wasn't as huge as it is today. It probably was bigger than Ruby, but not by that much.

One datapoint (that doesn't give the full picture), Pycon 2008 had 1k attendees [0], Railsconf 1k8 [1].

IMO It's really in the early/mid 2010's with the rise of "big data" that the Python community really became huge compared to Ruby's.

> I have never been a serious python user, so can't really compare

As an ex-pythonista, now Rubyist, IMO one huge factor was that Python 2 and 3 were developed concurrently for several years, that played in the messaging. Python 3.0 was released on 2008, Python 2.7 in 2010, and Python 3 wasn't really deemed production ready until 3.2 or 3.3 if I remember correctly. So there was a 4-5 years limbo. Whereas in Ruby's case, it was very clear to the community that 1.9.x was the future.

Also Python 3 did make some absolutely necessary breaking changes (like unicode), but also some minor syntax changes that made it much harder to support both with a single codebase for questionable gains.

e.g. the `try/except`syntax was `except Class, var:` in 2.x, and `except Class as var:` in 3.x. That kind of changes made it hard for packages maintainers to support both versions. Ruby 1.9 didn't have this problem. It was much easier if not trivial to write code that worked on both Ruby 1.8 and 1.9.

> typical python "minor" releases may actually have fewer backwards breaking changes than typical ruby "minor" releases?

I haven't seriously used Python in a long time, but from occasionally glancing at release notes, they seem to regularly deprecate things. Can't say if more or less than Ruby.

[0] https://pycon.blogspot.com/2008/04/part-2-attendance-registr... [1] https://www.endpointdev.com/blog/2008/06/railsconf-2008-repo...


> Ruby 1.9 didn't have this problem. It was much easier if not trivial to write code that worked on both Ruby 1.8 and 1.9.

Oh yeah, that is hugely important, and I agree ruby does this; it's usually not hard to write code that will work on at least the last handful of ruby releases.

The keyword arg changes initially made this difficult in one particular case -- and this was seen as a problem, so they introduced the `ruby2_keywords :method_name` thing, to make it possible with an opt-in to make sure you had identical behavior in all versions, before the ruby version was released that would have made it impossible.


> I haven't seriously used Python in a long time, but from occasionally glancing at release notes, they seem to regularly deprecate things.

The meaning of deprecation changed a bit. Before 3.6 or so things were deprecated, but just left in and maybe eventually removed when they broke for good, which would rarely happen. Nowadays deprecation means it will actually be removed after two releases (years), and a lot of stuff was deprecated basically just because it was old. I don't think that's an amazing signal to send, honestly.


Well, the most vocal ruby community was RoR users, while python even back then was used by multiple camps.

Ruby done a smart upgrade path - new features in 2.x and breaking changes in 1.9.x. Ruby also got a "new" VM (it wasn't new, but it was the new default VM).

Before 1.9.3 the best way to deploy ruby was Passenger with RubyEE. RubyEE was based on 1.8.7 and with all the improvements that the mainline ruby implementation got there was no reason to maintain RubyEE fork. The 2.0.0 release meant to be 100% compatible with 1.9.3 and had ABI version 1.9.1.

Also, prior 2.1.0 Ruby used versioning very different from SemVer, hence 1.9.x had multiple breaking changes in its lifetime. It didn't mean "patch" version back then.

I think the main difference is that the newer ruby was actually faster than the older ruby and gave plenty of reasons to move. The main pain point im ruby migration was that it became encoding aware. Python 3 wasn't as lucky.


I had the opposite conclusion.

This seems like a pretty minimal set of changes to get a 19 year old piece of software running.


Ditto. Having had to update python code over the years, this looked much easier than python migrations. To be honest moving earlier python 3.x code to current 3.x is comparable, so good job ruby devs.


Strongest possible disagree.

There was some pain around Ruby 1.8->1.9 because of string encodings, but no huge schism ala Python 2/3.

Did you experience both transitions or is that just your feeling based on this article? If anything, I think the article proves how relatively painless the transition was.


I agree with your strong disagree.

I know, and help maintain, Ruby code written for 1.8 that hasn't been touched and is running fine in 3.2. No breakage along the way.

To be fair, that's not always possible, but Ruby's changes haven't ever been drastic.

I've not had as much experience with Python as a programmer - more as a user of apps written in Python, and I sense there's a bigger issue there than Ruby has ever had. It seems like a frequent problem that something that works in Python 3 doesn't work with Python 2 and vice versa.


Hard disagree. For a moment there it wasn't clear if there was going to be a hard Python 2 vs 3 fork and people would literally refuse to migrate to 3. There was never such a moment in Ruby. The migrations from version to version were relatively smooth except 1.8.6 to 1.9 MRI -> YARV


But it was done slowly enough not to piss anyone off.


The transition to Python 3 took a very long time. I often wonder if they would have saved themselves a ton of grief if they had done it quicker.


That's not what they meant. Ruby made breaking change by breaking change, and it was still easy to have gems work with both ABI versions. Python 3 had changes that made it necessary challenging to support both, all while being slower (IIRC).


Ruby is used a lot less so the base population of people able to complain is much smaller.


It's used less but those who use it mysteriously make a lot more money.

I know which group I'm happy to be a part of.


From an outsider's perspective (I've written a little Python, but not enough that I consider myself a Pythonista) I didn't think it was backward incompatibility that was the problem as much as community libraries and frameworks not moving forward to v3 for a long time.


If the author reads this, I think you shouldn't bind space to the side menu, I (hope) many people know you can scroll with space.


I dont experience that. Perhaps you pressed tab once beforehand to focus the menu?


ah, yes, but since there's no focus indicator it's not obvious


Looks like it has a focus indicator to me, although I can see why it might be confusing: https://imgur.com/a/Mc6A7Dn


Yeah, I guess I pressed tab right away and thought it was the design of the button.

I think it's better to keep the focus style as the default outline, and uniform.


I don't know if they fixed it really quickly, but space does the normal scrolling for me, on Chrome and Safari.


RoR was cool way back when, but these days I'm much happier using nodejs with a framework like Adonis


Ooof...I'm not honestly. I don't do Rails that much these days but when I do it's just crazy how productive I can be. I think it's still a great fit for a huge number of web applications.

If performance is very important there are better options like Rust or Go, but otherwise RoR is my go to. Pretty much everything you need is baked into the framework and it's still being actively developed. Some of the new stuff is pretty great like Turbo frames and streams.

I respect that you enjoy working in Nodejs but Nodejs frameworks are just so barebones in my experience. I haven't tried Adonis but I still remember when I was looking how to parse query params in Koa. The official docs said to use a third party lib or regex them yourself from the request string...like why would I even use a framework at that point?


I'm actually really excited about the direction Rails is going in. It's like it's shedding some of the complexity that was accumulated over the years in favor of things I can finally understand again.


I don't get people who voluntarily use JS outside a browser. I'd rather jump into a pit filled with rusty spikes.


That’s fair, but TypeScript coupled with modern JS dev tooling makes it actually a pretty palatable experience these days. Speaking as someone who used to want to jump into pits for JavaScript-related reasons.


Isn't that the difference, though? People who legitimately love Rails because of how productive they are, versus "pretty palatable".

It's wild how people insist on using less-good tooling because that's what they've heard they are supposed to do... and that that's as good as they are going to get.

It's not!


This whole blog post is essentially an advertisment for why statically typed languages can prevent such madness. I'm sorry -- it is not my intention to start another non-useful static/dynamic typing debate.

It seems crazy that when a new version of Ruby comes out, developers need to hunt for runtime errors (that may or may not trigger depending on the path the application took) to see what incompatibility needs to be fixed.

Take any decent statically typed language. When a new version of the language comes out, your code will not compile on constructs or functions that are no longer called or present or used the way they should.


Not being able to run a 15 year old codebase on a < 1 month old runtime without making some modifications has nothing to do with static or dynamically typed languages.

But first - why don't we point out that the bulk of the issues the author faced had nothing at all to do with types? The issues were primarily with syntax changes.

Regardless - with a statically typed, compiled language, you find these issues at compile time. With any other language you find them at runtime. Either way, you'd have to fix a whole lot before you deploy the code, and just because you prefer to find your exceptions at compile time doesn't mean that it's the best way to find them.


> just because you prefer to find your exceptions at compile time doesn't mean that it's the best way to find them.

It is indeed the best way to find them. At compile time, you get errors for all possible paths a program will take.

For dynamic languages like Ruby you will get an error only if the program takes a path through problematic code and then Ruby will flag the error. This means a runtime error could lie latent in your codebase for many more weeks and months. Only if a rare condition triggers a code path that contains the incompatibility. This is also why refactors in languages like Ruby are more difficult and conservative. As you're never sure you fixed everything.


This seems like a semi-moot point considering that after you've updated something you (presumably) also run an automated test suite and (have someone) test the application manually.

I've also had various occasions where code compiled successfully but no longer worked as intended.


The automated test suite can check many code paths but compilation of a statically typed language checks all possible code paths.

Put another way, automated test suites give you an extremely high level of assurance when using a statically typed language. When your test suite passes in the new version Ruby, you're happy but there still could be cases left that you've not dealt with in rarely triggered code paths/conditions.


The problem with this argument is that you never mention the costs or trade-offs of a statically typed language. You presume that you get the benefits for free and I'm certain that is not the case. The worst systems I've ever worked on were ones with complex and poor types and type hierarchies.


What are the costs of a Ruby incompatibility just waiting to be discovered in production ? You can't assume you wrote tests to exercise every possible branch in every method in every object ?

The costs of static typing are reasonable as long as you're not using a fancy dependently typed language. Ask companies that are maintaining long running software -- types help. The investment is there in the beginning, the payoff is over the lifetime of the project.

If types are complex and poorly defined, you can change them ! The compiler will help you evolve your system through type errors. If you have a poorly structured program in a dynamically type language like Ruby then it becomes more difficult to evolve your system fast and with confidence. You're always asking -- have I missed something out ?


I've been writing software for a long time in many different types of languages. I've led many software teams for a long time that use different languages and tech stacks. I have not seen any measurable difference in productivity or defect rate across different languages. I have also looked at all of the research on this and it is inconclusive at best.

Ultimately, I think that choice of language is one of the least significant predictors of outcomes, yet it's one of the most debated and obsessed over.

Edit: I thought it would be helpful to give examples of what I think is more important. Good CI/CD practives, good observability, robust test and staging environments, etc have been far more important in my experience than static vs dynamic language choice.


This is a great example of the difference between theory and practice.

In theory, you're correct and type errors could be hiding out in a dynamically-typed codebase after a language version upgrade, lying in wait to take your system down.

In practice, nothing like what you're describing has ever happened to me when working in large (1M+ LOC) Ruby and Python codebases.

In my experience the difference maker is testing, not type checking. I've worked in poorly tested Java codebases where upgrading a dependency still allowed the application to compile and deploy successfully, but then at runtime things started blowing up randomly. Meanwhile with well-tested dynamically-typed code I've never had any such issues.


I've been using rails for well over a decade (since version 2), and loving it. For the most part upgrades have been relatively painless by choosing to always lag a little behind on non-security related upgrades.

There have been times though when upgrades have been extremely painful. The massive shift between versions 2 and 3 (merb integration), the openssl shenanigans (ruby v2 -> v3), and the mimemagic / shared-mime-info licensing issues are the major ones that come to mind.

That being said, I use Elm on the front end a lot and the confidence it gives me about my code is like night and day compared to ruby (or javascript).


Java is “statically typed” amd has the same issues during runtime with different JVM versions.


People try to run JVM versions across 10 years and face "small" issues with builtins. GP's point was mainly about missing interfaces, missing methods, typos etc.


> People try to run JVM versions across 10 years and face "small" issues with builtins. GP's point was mainly about missing interfaces, missing methods, typos etc.

Indeed -- well put.

> Java is “statically typed” amd has the same issues during runtime with different JVM versions.

Nothing is ever perfect -- there can be some occational runtime incompatibilities when very old Java code is run on new VMs.

But the effort in moving between one Java version and another Java version that came out many years later is likely order of magnitudes less than if you did the same thing in Ruby. There is simply no comparison.


sure, take some scala 2 library and make it work with scala 3 / dotty with an effort similar to this one. or java 1.8 and 17. we'll wait.


Heavy Ruby/Rails user here.

We basically run type checking on major hand-offs between modules using DrySchema/DryValidation, a gem that makes it easy to set that up.

Without doing this, the integrity of the codebase erodes with scale.


I'm not sure I'd recommend DrySchema or DryValidation.

I've had real problems upgrading client's projects built using DryValidation prior to v1 in the past.

They changed the syntax of the validations just enough to break stuff and completely removed features like shared predicates. The upgrade process was very poorly documented with suggestions to go read random blog posts.

I think the replacement for shared predicates was supported be macros but even that is marked as likely to be removed in v2.

Just looking through some of the Dry changelogs the work BREAKING still features far more than I'm happy with. I'd be much happier to use Sorbet or an RBS based typing solution than work with Dry again.

It might have advantages over ActiveModel but ActiveModel is far better supported.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: