Ruby Best Practices - Method Arguments

To write readable and maintainable code, follow a few simple rules about argument ordering.
Tip 1: Optional arguments go last
When a method takes several arguments, and some of them have default values, make sure to put the optional ones at the end.
Take this method definition:
def foo(a, b = "val_b", c)
# some code...
end
Looks fine at first glance. But in practice, it’s impossible to call this method with values for a
and c
without also specifying b
. You end up writing:
foo("val_a", "val_b", "val_c")
…which defeats the purpose of setting a default for b
if you're forced to specify it every time. ¯_(ツ)_/¯
Solution: move optional arguments to the end
def foo(a, c, b = "val_b")
# some code...
end
Now you can call:
foo("val_a", "val_c")
No problem 😎.
Tip 2: Use keyword arguments (aka kwargs)
Tip 1 only gets you so far. What if you have multiple optional arguments and want to override just the last one? You’d still need to specify all the previous ones.
For example:
def foo(a, b, c = "val_c", d = "val_d", e = "val_e")
# some code...
end
Want to change only e
? Too bad — you still have to write:
foo("val_a", "val_b", "val_c", "val_d", "my_val_e")
Now imagine this in a large codebase:
foo(user.id, "DONE", true, false, 1, "always", true, post.id)
And you're left thinking: "Wait, what’s that false
again? What does the 1
do? Why 'always'
?"
Not ideal, especially under pressure in a production bug fix.
Solution: switch to keyword arguments when you hit 2+ optional params
Benefits:
- Argument order doesn't matter anymore
- You can ignore defaults entirely
The method now becomes:
def foo(a, b, c: "val_c", d: "val_d", e: "val_e")
# some code...
end
To change only e
, you write:
foo("val_a", "val_b", e: "my_val_e")
💡 BONUS: keyword args make your calls self-documenting. You don’t need to check the method signature to remember what each value means.
Ninja-level tip — the clean coder’s take:
If your method takes more than 1 argument, make everything after the first a keyword argument. Always.
Example:
def send_message_to(recipient, confirm: true, bcc: "[email protected]", cc: "[email protected]")
# ruby code
end
Here, the first argument is obvious thanks to the method name: you're sending a message to someone.
Called without options:
send_message_to("[email protected]")
Clear and concise.
Compare a non-keyword version:
send_message("[email protected]", false, "[email protected]", "[email protected]")
…with the keyword version:
send_message_to("[email protected]", bcc: "[email protected]", confirm: false)
And by the way, naming matters. Ruby reads like English — write code you can speak out loud.
If you adopt these habits, future you will thank you when you revisit the code to debug or tack on yet another parameter.