Kernel#send Should Be Removed ############################# :category: Ruby :tags: :date: 2020-07-24 07:07:00 +0300 Opinion: ``Kernel#send`` should be deprecated and removed from future rubies. ``send`` always felt weird when I was an active ruby developer but I have never seen any strong criticisms on it. It's either an attack on `all family of send-like calls `_ or a fierce defence that "using ``send`` is not a bad practice". Only `some style guides `_ argue against ``send`` because it may overlap with existing methods. This post was written after reading one of defences. .. TEASER_END .. _ds: https://brakemanscanner.org/docs/warning_types/dangerous_send/ .. _rsg: https://github.com/rubocop-hq/ruby-style-guide ``send`` belongs to a family of 3 methods that allow calling a method by its name as a parameter: * ``BasicObject#__send__``, the base method * ``Kernel#send``, alias for ``BasicObject#__send__`` * ``Kernel#public_send``, that calls only public methods My problem is only with the second one. What's wrong with ``send``? =========================== "What is the problem? After all it's only an alias!". Two arguments combined: * It breaks encapsulation * It looks perfectly normal Breaking encapsulation is a too common practice in a Ruby world. Monkey patch a library? Why not! ``bundle update`` now breaks the project? It always does that! Let's invent updating practices instead of not interfering with guts of external libraries. Non-public methods are not in any library's interface and are never covered by semver promise for example so you work with them at your own risk. If you do it regularly, it's a code smell. .. code-block:: ruby class DemoObject def update_date(value) # ... end private def update_caches(keys) # ... end end obj = DemoObject.new field = 'caches' # ...tons of code... Let's update a field dynamically: .. code-block:: ruby # clearly not what you might want obj.send :"update_#{field}", new_value # error here, at least it won't break something in an unexpected way obj.public_send :"update_#{field}", new_value But look again at these calls. The first one looks more natural, more logical and it is simply shorter. This technically makes this metaprogramming pattern unsafe by default. Maybe if they were named ``private_send`` and ``send``, this post wouldn't exist. Ok, maybe the library is bugged and there is some error with caches .. code-block:: ruby # this looks like a regular call # you may easily miss it: obj.send :update_caches, [:key1, :key2] # this looks like a code smell # so if there is no huge explanation # why it is done and when it can be removed, # you do `git blame` and go hit someone with a hammer: obj.__send__ :update_caches, [:key1, :key2] The second method clearly shows us that you're messing with some internals and you must have a good reason for that. This weirdly looking call is easy to spot even if you're looking through code without proper attention. ``public_send`` and ``__send__`` ================================ ``public_send`` ``public_send`` is fine as long as you `put only trusted data in the first parameter `_. It's a very useful and safe tool for metaprogramming. It's one of that parts that I love about the language. ``__send__`` First, ``__send__`` is a base feature of the language, a building block that cannot be easily removed or hidden. Second, as stated above, the method name with double underscores on both sides clearly shows that something hacky is happening and indicates a potential code smell.