My friend/co-worker Sam Jesso told me that he hates using flags to modify the behaviour of functions. It makes sense. Functions should follow the single responsibility principle and do exactly one thing. It makes testing and maintaining code easier because it keeps functions simple and concise. However, diving into almost any codebase will reveal that we often make exceptions and use flags.
Whether or not these exceptions are justified is not what I want to discuss. It would be impossible to come up with a set of rules or guidelines for when an exception makes sense because everybody’s code is different. But if you’ve already decided that you want to pass flags into your function, there is a simple trick you can use to make your functions’ interfaces more developer-friendly.
Rather than treating flags (or options) as separate parameters, we can group them into a single
Grouping options into a single object has several advantages over using separate parameters. To better understand these advantages, let’s take a look at a less abstract example…
An Example with Formatting Time
Here’s a simple function to get a formatted time string from a
Anyway, back to the example.
We have a function for formatting time and it does a great job. But now we want to have the option switch between 12-hour and 24-hour time. And we also want to exclude seconds in some cases.
No problem, we can just add some extra parameters to the function:
There are several issues with this approach:
- The parameters must be passed in a specific order. If we want to hide seconds, we must still pass a value for
is12Hoursbefore we can specify one for
- The parameters are unnamed. If the function is being called far away from the definition, it may not be clear what the parameters mean. We must go to the function definition to find out what the various
These issues make the function interface very difficult to read understand and they amplify the potential for human error especially when a function has many options because it’s easy to accidentally skip a parameter or mix up their order.
Refactoring with an options object
A simple way to fix these issues is to refactor the function to use an object for flags/options:
This approach solves the issues that exist with passing flags as separate parameters by:
- Exposing the flag names to the interface.
- Forcing developers to label the flags properly.
- Making the ordering of flags irrelevant.
- Allowing exclusion of flags when we want the default behaviour.
In addition to making the function more readable we’ve also made it maintainable because it’s now easier to add many flags to our
formatTime function without adding more and more nameless booleans, making the function calls unreadable. We could add flags for
showMilliseconds, or even an option to specify a custom delimiter to replace the default colon. Whatever flags or options we add, the function will remain relatively readable.
One More Thing…
Even though we’ve made the function’s interface easy to use and add to doesn’t mean that all the functionality for those parameters should be aggregated into a single function. Use your best judgement and decide when to delegate functionality to helper functions.