Ruby's hash is a Swiss-army knife
Since Ruby 3, the automatic coercion of keywords to a hash—the second example underneath "Passing Hash to Functions" in this post—is considered a legacy style and is generally frowned upon in new code. That is to say, code like the second call to `foo` here:<p><pre><code>    def foo(kwargs = {})
      kwargs
    end
    
    foo({k: 1})  # ok: passing hash argument
    foo(k: 1)    # ok: keywords coerced to hash
</code></pre>
One of the strongest arguments for avoiding this sugar is that it makes the code more brittle in the face of future changes. In particular, in the example above, if we add a new keyword argument to `foo`, then any call which omitted the curly braces will break, while calls which used them will keep working fine:<p><pre><code>    # added a new keyword arg here
    def foo(kwargs = {}, frob: false)
      kwargs
    end
    
    foo({k: 1})  # still ok: `frob` defaults to false
    foo(k: 1)    # ArgumentError: no keyword: :k
</code></pre>
This is touched on in the blog post describing the extensive changes made to keywords in Ruby 3: <a href="https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/" rel="nofollow noreferrer">https://www.ruby-lang.org/en/news/2019/12/12/separation-of-p...</a>