Self-Documenting Function Calls
Tags: functions, codequality, javascript, tutorial
Published: 2020-02-24 18:51:33

There are a few languages I've used that have named parameters.  Named parameters can be pretty cool because:

1. They allow you to pass the parameters into the function _in any order_.

1. They foster self-documenting code because, at the point where the function is called, the parameter values _and the names of those parameters_ are spelled out right there in the code.

Alas, JavaScript (my most-favoritest coding language ever - until I find a new most-favoritest coding language ever...) doesn't have named parameters.  In fact, _most_ languages don't.  But that's OK.  There are still ways that we can write some awesome self-documenting code without named parameters.

Let's imagine that we have this function:

```javascript
const searchRecipes = (
   searchText = ''
   ,includeSpicy = true
   ,onlyVegan = false
   ,includeSponsoredResults = true
) => {
   // do all the recipe-search magic here
};
```

We have a pretty good start to a clear, concise function.  The name of the function seems self-explanatory.  The parameters are given clear names that aren't lazily abbreviated (e.g., no `srchTxt`, `inclSpcy`, `onlyVeg`, or `inclSpnsRes`).  

We also have default values on all of the parameters.  This helps us write cleaner logic - because we can assume a default value if none was provided.  It also helps our IDE to suss out potential code smells (like, for example, if we tried to pass an _object_ as the `searchText` argument).

## Losing the Context When the Function is Called

If there's any "problem" with this function, it's not in how it's _defined_.  It's in how it's _called_.  Unfortunately, JavaScript doesn't (by default) give us the tools to control how a function is _called_.  We can only really control how it's _declared_.  Once we've declared it and it's out there, "in the wild", for any devs on your team to use, it can possibly be called in any ol' haphazard way.

Imagine that, somewhere else in our code, we need to invoke this function.  So we do it like this:

```javascript
searchRecipes('linguine', true, false, false);
```

Suddenly, all of the glorious _context_ in the function declaration has flown out the window.  Any casual onlooker who reads this line of code can probably guess that we're searching for recipes, and that we specifically want recipes that have something to do with "linguine".  

But... `true, false, false`??  What the hell does _that_ mean?  And what happens if we start flipping those Booleans?  There's no way to tell by looking at the function invocation.  

(Side note:  You may be thinking, "Well, in my Super Cool IDE, the names of those variables are displayed for me - either statically, or when I hover over the function call."  To which I'd say, "Yeah, that's great.  I have a Super Cool IDE as well.  But well-written code isn't dependent upon whether _other coders_ are reading it through the same IDE that you are, with the same settings that you're using.")

## Dragging the Context (Kicking-and-Screaming) Back Into the Function Call

You may think that there's not much we can do about this.  After all, the function accepts a string followed by three Booleans.  And that's exactly what we provided to it.  We can't help it if those values have no implicit _context_, right?  

Well, no, not exactly.  We _do_ have some significant control over how we call the function.  The technique I'm about to illustrate is incredibly _simple_ and _easy_ to use.  And yet, I rarely-if-ever see this in the codebases I'm exposed to.  I'm talking about defining inline variables _in_ the function call, like so:

```javascript
searchRecipes(
   searchText = 'linguine'
   ,includeSpicy = true
   ,onlyVegan = false
   ,includeSponsoredResults = false
);
```

The invocation above does the exact same thing as `searchRecipes('linguine', true, false, false)` but this approach takes all the mystery out of those Booleans.  We now know, just by reading the function call, that we're searching for linguine recipes, that may be spicy, that are not restricted to vegan dishes, and we don't want any sponsored results.

There _is_ a bit of a problem with this approach, however.  You sharp-eyed JS hawks will probably notice that I'm assigning values to undeclared variables.  You can do that in JS code that is not running in "strict mode".  But if you're using "strict mode" (and you absolutely _should_ be using "strict mode"), the previous example won't compile.  

Most modern frameworks - like React - are automatically running in "strict mode".  So whether you realize it or not, you're probably writing-and-deploying your code in strict mode.

In strict mode, we must first declare our variables - with a `const` or a `let` - before using them.  So what if we try it like this?

```javascript
searchRecipes(
   const searchText = 'linquine'
   ,const includeSpicy = true
   ,const onlyVegan = false
   ,const includeSponsoredResults = false
);
```

Umm... no.  Sorry.  That doesn't compile either. 

So are we back to calling this as `searchRecipes('linguine', true, false, false)`?  No.  We have other options.  The most obvious one is to have those variables declared _before_ we get to the function call.  That would look like this:

```javascript
const searchText = 'linguine';
const includeSpicy = true;
const onlyVegan = false;
const includeSponsoredResults = false;
searchRecipes(searchText, includeSpicy, onlyVegan, includeSponsoredResults);
```

OK, we've satisfied the "strict mode" compiler. We've preserved all the valuable _context_ that goes along with those argument names.  However, I will admit that this feels like a lot of extra variable definitions to load into the file.

Obviously, this approach works wonderfully if you already had those variables defined somewhere else in the previous instructions.  But this might feel to many as too much "overhead" if you're only defining all of these variables just for the sake of readability.

Another way to achieve this effect, while still minimizing the overhead of purely-documentary code, is to define these values in a global constants file.  You may not _have_ such a file.  You may not even _want_ such a file.  But if you do, it would look something like this:

```javascript
// constants.js
const constants = {
   includeSpicy : true,
   doNotIncludeSpicy : false,
   limitToVegan : true,
   doNotLimitToVegan : false,
   includeSponsoredResults : true,
   excludeSponsoredResults : false,
}
export default constants;
```

```javascript
// constants was imported above
searchRecipes(
   'linguine'
   ,constants.includeSpicy
   ,constants.doNotLimitToVegan
   ,constants.excludeSponsoredResults
);
```

Notice that, to make the code optimally-readable, we defined _two_ potential variables for each of the Boolean values that must be passed in.  One for the `true` condition and one for the `false`.

Of course, there are still trade-offs in this approach.  It implies the importation of a common constants file. It still requires that the variables be stored _somewhere_.  Also, it can make the logic a bit difficult to follow (notice that to _exlude_ spicy dishes, we're using a `constants.doNotIncludeSpicy` variable that evaluates to `false`).  So if even _this_ approach doesn't suit you, we'd still have to look for other options. 

Thankfully, there's still one more method that satisfies the "strict mode" compiler and requires very few extra lines of code.

## Object Magic

JavaScript, in "strict mode", requires us to declare our variables before using them.  But _objects_ provide a sort of backdoor workaround to this.  The object itself must be declared.  But we can define properties on that object _on the fly_.  That's useful in our present example because now we can do something like this:

```javascript
let arg = {};
searchRecipes(
   arg.searchText = 'linguine'
   ,arg.includeSpicy = true
   ,arg.onlyVegan = false
   ,arg.includeSponsoredResults = false
);
```

So all we had to do was add one extra line of code above the function call to define a dummy object that will serve as a bucket for any of our inline argument definitions.  And the nice thing is that if you have 50 more function calls that are similar to this (in the same scope), you can reuse that `arg` object as often as you like.

Now we have a function call that's fully self-documenting.  _And_ it complies with ES6's "strict mode" requirements.  

## When Do We Need This??

I'm not advocating to use this approach on every damn function call.  In fact, it probably doesn't need to be used on _most_ function calls.  Consider the following example:

```javascript
const searchResults = searchRecipes(userSuppliedSearchString);
```

We know from the original function definition that the `includeSpicy`, `onlyVegan`, and `includeSponsoredResults` arguments are optional.  Given the name of the function, and the name of the variable that's passed into the first argument, it's pretty clear what's happening here.  I don't need anyone to explain to me that we're searching for recipes based on a user-supplied value.  So in this case, it's probably overkill to explicitly name the argument as we pass it into the function.

In fact, _most_ function calls that pass in only a single argument _should_ be fairly self-explanatory.  And the readability factor is further enhanced when our arguments are already stored in descriptively-named variables.

The most common use-case for this approach is when you have a function that has multiple arguments - especially when many of those arguments are switches/flags/enums that don't make any intuitive sense on the calling end.  

This is especially true of _built-in_ language functions that may require an obtuse value (like a Boolean) that is almost-never self-explanatory when looking at the invocation.  That's why I used Booleans in the example - because Boolean values, passed _into_ a function, almost never make intuitive, easily-readable "sense" when you are simply perusing them from the calling end.