However, there definitely is a burden about how much
testing you have to write. I generally don't want to
have to test every branch of my program to make sure
a string doesn't slip through where an int should be
or that variables are initialized and not null, etc.
This has not been my experience!I've been writing Ruby full-time for about six years (including one of the largest Rails apps in the world) and I don't find this particular aspect of Ruby to be a problem whatsoever relative to statically-typed language. Ruby is famously easy to write tests for. Creating mocks in a dynamic language is such a breeze. There are plenty of problems with Ruby but an increased test suite burden is NOT one of them. "Confident Ruby" by Avdi Grimm is a great read in this vein; not about testing specifically but writing confident Ruby code in general. Part of it is simple, human-friendly code hygiene. Give your arguments descriptive names and use keyword params when possible. If you have a method: foo(user_name:, height_cm:)
...then you'd really have to be asleep at the wheel to write something like this elsewhere in your code: foo(user_name: 157, height_cm: "Smith")
And so you don't need to write your code or your tests with any real extra level of paranoia. If somebody does pass a string to `height_cm:` it will return a runtime exception, just like it should.Of course, I do get type errors all the time in Ruby, but that's when I'm parsing JSON or something and that's generally not something static typing's gonna help you with anyway. Now... there ARE places where I miss strong typing in Ruby. One, I miss having extremely intelligent IDEs like you can have with Java or C#. I absolutely loved Visual Studio and Resharper in the C# world. The amount of reasoning it can do about your code and the assistance it can give you is absolutely bonkers. Two, obviously, there is a price in runtime execution speed and RAM usage to be paid for dynamic typing. I don't find raw execution speed to be much of a bottleneck in a Ruby app because Postgres/Redis/etc are doing all my heavy lifting anyway. RAM usage and app startup times with large applications is more of a real-world issue. |
First, if you're writing any kind of big code, than somewhere, in your code base, someone else is writing a code where the user name is spelled `username`. Or `user`. You don't have to be "asleep at the wheel" to not remember, or not know, which is which. So you're going to type the wrong one (not necessarily on purpose), and you'll get a runtime error. Not that bad, sure, but you'll get it.
Also, at some point, you'll realize that a string is not the ideal way to represent a user name [1], and some of the functions that deal with users are going to start returning a Struct %User{first_name: ..., middle_name: ....}. Or will it be a Map %{"first_name" => ...} ? And surely you're going to track all the calls of `foo` to fix them. And all the calls of all the calls of `foo`, because, who knows ? And suddenly you're doing the mental job of a static type checker. Surely you're not "asleep at the wheel" anymore, because you're doing the work of the wheel by manipulating the gears by hand.
Bottom line: I'll gladly admit that I'm too old and stupid to do that anymore. I had typechecking in the 90s. Give it back.
[1]: https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-...