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.
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.
I've also had various occasions where code compiled successfully but no longer worked as intended.
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 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 ?
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.
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.
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).
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'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.
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.
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.