ryanmcdermott / clean-code-javascript

:bathtub: Clean Code concepts adapted for JavaScript
MIT License
91.61k stars 12.29k forks source link

Function arguments (2 or fewer ideally) #199

Closed garystephens closed 7 years ago

garystephens commented 7 years ago

"Limiting the amount of function parameters is incredibly important because it makes testing your function easier. Having more than three leads to a combinatorial explosion where you have to test tons of different cases with each separate argument."

That's true, but the example "Good" code shown consolidates 4 parameters into an object, but they are still four separate input values, with the same number of possible permutations that need testing. So I don't see how this "make testing your function easier" (as compared to the "Bad" code example)?

pankajpatel commented 7 years ago

IMO it would completely depend on the role of those parameters. For example if those parameters are there to fill the placeholders, then it is easy to test. But if they are boolean ones, then it would require more testing.

Moreover if function execution is strict i.e. requires all params (not more, not less) then it is easy to test but it would go difficult when function is coded otherwise.

And as Boolean params are already covered as bad practice, this section will more or less gets doubtful.

joshystuart commented 7 years ago

I completely agree with @garystephens, it's shifting complexity from arguments to an object. What you are also doing is forcing the caller of the function to know about the implementation ie. The caller now has to know what properties the object requires and what ones are optional in order to be valid, whereas, a function signature displays clearly what is required and what is not --- especially with ES2015 and using argument defaults eg. (required, notRequired = 'some-default') => {}.

Another byproduct is, since this is Javascript, that the passed object can contain anything, which usually results in poorly thought out function scope eg. "Oh I now need loginButtonText, signupButtonText and username in my Menu, I'll just put them in the object too"; it's still just 1 argument, but the complexity has increased significantly. Combining the arguments alone does not solve the complexity issue.

A better example is:

// BAD
function createUser(name, email, addressLine1, addressLine2, city, state, country)

// Good
function createUser(name, email, address)

function createAddress(line1, line2, city, state, country);

So now you only pass 3 args, name, email, address and address is an object (DTO) that contains line1, line2, city, state, country.

Note; For an address to be valid, it requires all of those properties (well, line2 is debatable), so there's no reason to reduce this down to a single object argument ie. Even though there are 4 arguments for address, I would still suggest this is valid, despite the guidelines in "Clean Code" requiring less.

I don't believe you need a rule to say "reduce args to a maximum of 2 at all costs" because there are so many exceptions to that rule. I believe if you follow the single responsibility "rule" in SOLID, your arguments will naturally be reduced as a result. Large numbers of arguments typically suggest that your function is doing more than 1 thing. If you encapsulate the data and abstract the logic into other functions, it usually reduces the number of arguments any one function requires.

ryanmcdermott commented 7 years ago

Shifting to an object does keep some complexity you're right, it makes sense in many cases to break the function into multiple functions if you need a lot of arguments (that's probably another good subsection to have) There are three main benefits to using objects over a bunch of individualized paramters:

  1. Objects have signatures. If you add type annotations later, via Flow or TypeScript, you can easily make sure your caller is obeying an interface.
  2. Objects don't have to obey parameter ordering
  3. It's easy to add on fields to an object at a later date, which won't force your callers to have to change their parameters.