4 ms·
I hate Ruby with a passion (it's also the language I mostly use in my day job currently). It's not necessarily the language itself, which is a reasonable langu
by jupp0r 3y ago
I hate Ruby with a passion (it's also the language I mostly use in my day job currently).
It's not necessarily the language itself, which is a reasonable language, it's the patterns that are common in the ecosystem:
1. Use of inheritance for code sharing. It makes it extremely hard to reason about any piece of code I am looking at. Where does the `param` variable come from? Is it injected by any ancestors of the class I'm looking at? Who knows, I have to use the debugger to find out. I cannot reason about the code without it. Of course you can use Ruby while preferring composition over inheritance, it's just rarely done by the community. Other modern languages like Go and Rust wisely leave out inheritance for code sharing in their object model and arguably have much more readable (albeit verbose) code.
2. Global mutable state is everywhere. Not sure if it's Rails architecture that encouraged using global state as request local state (until they found out that this makes concurrency hard), but Ruby codebases are full of global mutable state. It's everywhere and again makes it hard to reason about dependencies between objects and how they interact with each other. Again, this is nothing that the language forces people to do, it's conventions.
3. Overuse of meta programming. Ruby's metaprogramming is really well done in my opinion. The problem is that lots of people
want to use it and do so in places where it doesn't provide enough value to justify the costs. It's an authentication library. It doesn't need it's own DSL.
- 38 3y ago> Go and Rust wisely leave out inheritance for code sharing in their object model Bad news buddy: https://gobyexample.com/struct-embedding https://gobyexample.com/struct-embedding At first I loved it, then quickly realized as you did that it makes code essentially unreadable.
- masklinn 3y agoThat is very much not inheritance. It’s literally just sugar for delegation: calls which delegate to embedded structs execute in the embedded struct’s context. And the embedded struct has to be part of the struct definition, it can’t be snuck in by reopening the class and include-ing new bits.
- ajmurmann 3y ago> Other modern languages like Go and Rust Ruby's first release was in 1995. Go in 2009.
- jupp0r 3y agoI see your point. The question is what happened between 1995 and 2009 that lead to different choices in language design? Java?
- ajmurmann 3y agoA lot happened. Not only did we get new languages and learned from them, but existing languages and how we use them evolved. Looking at what influenced Go and Rust and where they try to fit, evolution of best practices for C and C++ probably played a large role. Wikipedia tells me that one of the influences on Rust in fact also was Ruby itself.
- vidarh 3y ago"param" isn't a variable unless you defined it in the current method - it lacks a sigil so if it's a variable it must be a local one. So it's more likely to be a method, and so you can usually find where it was defined with self.method(:param).source_location (or use pry or irb, or rdbg, and drop to a debugger session and you have easy access to nicer methods). If you don't want to run a tool like pry or rdbg, then sure, Ruby will be painful for you, just the same way as refusing the use the best available tooling will make working with most languages. As for disliking Rails, join the club, plenty of Ruby users dislike Rails too.
- jupp0r 3y agoSorry for the contrived example. My whole point was that you have to use the debugger in order to read code. I'm not unwilling to do it, it's just extremely unproductive vs reading code that's understandable without having to reverse engineer it.
- vidarh 3y agoIf running a tool like pry (which is not a traditional debugger, for that we have rdbg) slows you down, then that is a tooling problem. Instead of just reading the code, you can read it and explore the API live. To me that's usually far faster beyond a couple of pages worth of code, because you see how the code actually works rather than how you imagine it should.
- 1letterunixname 3y ago[insert my own personal bitching about the pains of procedural configuration management using Ruby at scale and the lost causes of rubocop and rbs] a. Yes, global mutable state is anti-functional style, side-effect-oriented, god function/object programming and it sucks. b. The lack of an ability to seal objects, classes, and modules from further modification is a problem. c. Ruby 3 should've broken compatibility by adding gradual typing. d. Nonorthogonality - Too many ways to do the same thing. e. Dying community - Too much broken code out there. f. Ruby was "Perl5 2.0" in most senses. For type-safer things that look like Ruby, Crystal is an almost viable, interesting alternative. Otherwise, I have evolved past Ruby because of the learning curve for others, too many people hate it, too many maintainers have attitude/professionalism problems, and there are more supportable alternatives for most use-cases such as rust, elixir, and bash. Sometimes, it's still useful to use ruby for some quick text formatting or a quick CLI utility in the space of awk, perl, or sed. The world mostly uses js and python. One can either be a hipster with obscure tools or get things done with the lowest TCO/highest ROI.
- vidarh 3y ago> b. The lack of an ability to seal objects, classes, and modules from further modification is a problem. "freeze" has been there from the very start". You can freeze objects, or individual classes and modules (because, hey, they're objects) just fine. I'm all for freezing more stuff, and the migration towards freezing string literals was good, and you can freeze very aggressively in your own code, including system classes. > c. Ruby 3 should've broken compatibility by adding gradual typing. If they had, it would've split the community. Some of us would've forked it. Any degree of gradual typing in Ruby will need to be extremely cautiously approached and/or mostly optional, as it strikes at the heart of what makes Ruby what it is. There are places it would work and be fine, and there are places where it would massively hamper productivity. > For type-safer things that look like Ruby, Crystal is an almost viable, interesting alternative. Crystal has a fraction of the community and too many pointless differences to Ruby for many of us to consider. > The world mostly uses js and python. One can either be a hipster with obscure tools or get things done with the lowest TCO/highest ROI. Funny. My career has spanned multiple "the world mostly uses $language" phases, and it's never affected my TCO/ROI whether what I've happened to use matches the current $language. What has mattered is picking a language the right libraries are available for and that I'm comfortable in. Sometimes I only get one of those, and sometimes that means picking a different language the Ruby, but writing composable tools means it's quite rare, and each time makes it rarer.
- IshKebab 3y agoRuby code seems to be designed to be hard to follow. Not only is it dynamically typed, but it seems like identifiers are dynamically generated, which makes it impossible to search for them. Here's a challenge: in the Gitlab source code, tell me where `external_file_project_job_artifacts_path` is defined. It's referenced in a few places but it isn't defined anywhere. And yes I know in other languages you can use codegen to construct unsearchable identifiers in the same way, but Ruby seems to actively encourage it.
- zemptime 3y agoThis made me curious. Having never read the gitlab code before, and on mobile, took all of about 30 seconds to find https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/routes/project.rb?ref_type=heads#L83 https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/ro... Those are some pretty clean routes!
- IshKebab 3y agoPerfectly illustrating the problem! Now try to imagine finding that if you are unfamiliar with Ruby routing.
- ilrwbwrkhv 3y agoI think another major problem which people do not mention too much is that the ruby community went pretty deep in the whole solid principles, uncle Bob refactoring rabbit hole. Because of that a lot of people still have major bad practices set in their heads such as a method shouldn't be longer than 5 lines etc. Most rails code bases that I have seen usually has had a senior ruby lover refactor things in such a way that one thing is always calling another thing and even though the actual working business logic might be 12 lines which should have been linearly written, is spread across 5 files with objects being new'd up everywhere making it really hard to follow.
- jupp0r 3y agoMost Ruby code I've worked with does not follow SOLID principles. Especially the Liskov substitution principle is almost never adhered to when using inheritance as a way of code sharing.
- emmelaich 3y agoI really like some bits of Ruby but I kept hitting really annoying parts. e.g. salary = 100 bonus = 10 total = (bonus + salary) => 100 Argh. The real example was more complex but you get the idea.
- jb3689 3y agoWow, I've been using Ruby for a decade and have never run into this. If you are dealing with a codebase written by someone else, then Rubocop probably has a rule for detecting these gotchas. I'd run it across the whole thing and burn down the flags.
- emmelaich 3y agoFWIW, I applied Rubocop, it said: Lint/Void: Variable bonus used in void context. Web search for 'ruby void context' finds a bunch of WTFs.
- whalesalad 3y agoGreat points. I also admire Ruby as a language (Eloquent Ruby is probably my fav programming book at this point in my life) but have had absolute nightmare scenarios thanks to terrible engineers who are not good at software architecture. Rails is a blessing and a curse in this regard. 99% of the time when I get air dropped on a big piece of non-rails ruby software it's going to 1. be a headache and 2. earn me a tremendous number of billable hours trying to fix it. A lot of dev/consulting shops that popped up after the advent of Rails began using it for absolutely everything and none of them knew what they were doing architecturally.
- rails_defector 3y ago> Global mutable state is everywhere Agreed 100%. This practice pushed me out into Rust/Go and I don't see myself returning unless corrections are made. I strongly believe Shopify, Stripe, Rails need to come together and eradicate this blight from the ecosystem if they wish to see new adoption. No other ecosystem finds this acceptable behavior and is actively discouraged.
- jupp0r 3y agoThe inevitable move to support concurrency via Fibers will be a forcing function for this. With the state of the ecosystem I don't know if all the libraries in your transitive dependency hierarchy will ever get rid of their globals though. If not, you'll have funny race conditions between multiple Fibers in external libraries to debug forever.