Writing code without thinking will cut you either way.
Things like method missing are exactly the right solution for a certain set of problems. If used thoughtfully, they’re amazing. If used thoughtlessly, bloody mess. But this is true in every language.
I think it's the most enjoyable language I've used. With care and feeding it's amazing. I would absolutely not want to use it in a large company, that would be a walking nightmare.
Avoid mutating arguments.
Avoid monkeypatching.
...
Avoid needless metaprogramming.
Prefer public_send over send so as not to circumvent private protected visibility.
Write ruby -w safe code.
...
[0]: https://ruby-style-guide.shopify.dev/#generalEarly in my career I made my own toy ActiveRecord implementation and it's full of define_method and was literally the first time in my life that programming felt like a real superpower.
If we were chefs a knife would be your laptop, your editor, your cli tools. Not your programming language.
A better metaphor would be the lumber in a building or the soil in a garden. I don’t know what I’d call ruby in this metaphor, maybe “lightweight wood that splinters”…
That being said, the original "Sharp Knives" piece is talking about powerful language features and whether to ban them in your kitchen, so yes, it has plenty of effect on their coworkers. Because it's a shared decision to not ban sharp knives from the kitchen, but trust and educate, and realize that people who're doing harmful things can do so with dull knives too. It's about the techniques you allow in your codebase, it doesn't propose that ruby is one "knife".
And programming language features does not, generally, end up on the plate of the customer either. The fact that this site is written in a language that allows even more powerful meta programming than ruby is not bleeding through in the sense that we now have the tools of the language available to us. We haven't been served sharp knives. We've been served the consequences of the engineering that went into it, and the people doing that engineering had the opportunity to leverage or ignore the sharp knives of the language. That language being lisp, I'm pretty sure they had at it.
Point being: In Ruby, sharp knives are too accessible, come with no warning or even encouragement.
Yet contrary to actual sharp knives (or guns pointed at feet) the feedback loop is slow. When you shoot yourself in the foot, or cut your finger, you feel it right away and (hopefully) adjust before more accidents happen. With Ruby, a footgun is fired today. Or a sharp knife carelessly tossed in the bag yesterday. But you'll feel the pain in months or years only. And probably not even you, but the person who followed your follow up.
Sharp knives are cool. But they need education on their dangers and responsibilities.
If we're talking about Rails at least, then meta-programming/magic/indirection, all that should be very rare in application code. It makes sense sometimes in more abstract libraries, where you have to handle flexible problems, but in application code it's really worthwhile to just be straightforward and verbose.
It creates code bases that seems to have a tendency to turn into sprawling messes. I appreciate that it has driven a lot of people to Ruby, and in that way it's an amazing and impressive success, but recruiters get it somewhat right for the wrong reason when they sometimes list Rails as a "language".
I'm very happy to use "magic" in Ruby, but it should be high-impact, low code, and contained. E.g. I'm very comfortable with the "magic" in my window manager that turns event classes received from the X11 binding into method call on a target class, so that MotionNotify turns into target.on_motion_notify(event) without me having to specify every event class. It's simple, small, and let me cut a lot of code.
A lot of non-Rubyists would hate it, though.
The irony, though, about your complaint is that Rails code is some of the most verbose Ruby code you'll find, because it's geared at making cookie cutter creation of sites easy, and so a lot of patterns involve an excessive separation into vast amounts of low-impact, verbose classes that do way too little (the obsession with some Rails devs of writing "service classes" for things that should just have been a single method, for example, drives me crazy - yes there are valid uses of service classes, no almost no uses of them in Rails apps are reasonable)
They’re still better than nothing, though. Method-mossing and friends played a critical role in making a framework like Rails possible in Ruby. In contrast, languages like Python and JavaScript just aren’t quite expressive enough to do it. A lot of energy went into attempts over the years, too! At best, something like Sails could get 2/3 off the way there and offer significant advantages on another axis, like making websockets first class.
I think a lot of the metaprogramming abilities Python has are just used less often than in Ruby. Not because it is less expressive, but because it has a culture of simplicity whereas brevity and generality are more valued in Ruby (Rails) culture.
https://stackoverflow.com/questions/7079855/are-there-techni...
Result was objects that shared no structure, no field
Unfortunately some people started following without full understanding. I wasn't at forefront of things back then, so I only noticed when the people who did know better started to writing about abuse of this technique.
The main complaint about method_missing has always been the inefficiency. The class hierarchy is walked every single time and only then is method_missing called. The use of define_method is a pretty neat solution to that problem. The method that wasn't found gets defined and it doesn't rely on method_missing again. Like a cache.
Personally, I've never used method_missing. Might have missed opportunities to write clever code, but I'm super happy being boring and explicit.
Tangentially, I've only used meta programming twice. Both where the alternative was cumbersome, and it was unlikely to cause confusion.
Ruby gives some exotic tools but doesn't force you to use them if you prefer not to.
If the set of messages you need to handle dynamically is to only ever be small, define_method() is the better choice, but when it is not, I'll take careful use of method_missing over some monstrosity that ends up emulating the same machinery with lots of extra code any day.
If you struggle with debugging due to method_missing, consider reviewing how you debug code, as it wouldn't even make my top ten of debugging challenges in the Ruby code I've worked on in the last 17 years of daily Ruby use.
All the most heinous bugs I've had to deal with in my life were about magic in ruby. I've worked in 3 of the biggest rails codebases in the world, and in the end we had build checks to deny code with magic.
Software code should be optmised for reading and debugging, not looking nice for first time writes.
Ruby is "an offender" in this to you because you hate the pattern that untangles the huge unholy mess that less dynamic languages use instead. E.g. hacky workarounds like whole class hierarchies or (marginally better) maps from values you dispatch on to first order functions/closures
I have worked in over a dozen languages, and I've never seen a medium to large sized project where some form of dispatch mechanism hasn't been part of the codebase somewhere.
And I'll take the simplicity of dynamic dispatch over hand rolling verbose mappings any day.
> All the most heinous bugs I've had to deal with in my life were about magic in ruby. I've worked in 3 of the biggest rails codebases in the world, and in the end we had build checks to deny code with magic.
I have no like for Rails, it encourages a lot of awful shit, but I also find this really curious, because the backtraces will clearly indicate you've passed through method_missing, you can breakpoint requests and attach remote debuggers to in flight requests, and execute code in-context in those requests.
If that's too complex, sure, maybe a large Rails code base isn't right for you. I don't want to work on Rails codebases either, but because of Rails, not Ruby, nor because the dynamism makes it any harder to debug. > Software code should be optmised for reading and debugging, not looking nice for first time writes.
I agree,and it's exactly why I love Ruby.
As a dynamic language, Ruby gives you a lot of tools to create fully expressive code. This is just one aspect of it that you can, but don't have to, use.
This argument isn't entirely true anymore. The Ruby ecosystem has mostly moved away from meta-programming, and embraced classical OOP. Rubyists now tend to avoid `method_missing` and `const_missing` when possible, and abandoned the craze of making everything a DSL. The most meta-programming that Rubyists still use on a regular basis is defining `self.included` on modules in order to inject other modules. Also, Rubyists have embraced API documentation, such as YARD, in order to document and communicate any "magic" that might exist or occur.
Ruby gets a lot of criticism for embracing magic and indirection ("Ruby, the bad parts of Perl"). It has some good parts (no function colors, for example), but the ecosystem is the biggest problem by promoting magic. method_missing is not a good part of Ruby, it's a very bad part that you should only use if you know it's bad _and_ you know you still have to use it. I wish all tutorials would say this. "You can do this with Ruby, but don't, because your coworkers debugging an outage for an hour will hate you once they find out the bug is caused by method_missing".