Refactoring long lists of parameters
There seems to be no agreement in the software engineering community about how many parameters are too many for a function.
This should come to no surprise.
The ideal number of parameters for a function need not be the same as the ideal number of parameters for another function. Furthermore, even if we focus only on a single function, the perceptions of different software engineers (who may have different backgrounds) will vary.
The issue is very subjective.
Proof of this subjectivity is that, if we look at several well-known resources, we will find different opinions.
The Code Complete book advises us to limit the number of parameters to about seven. The reason is that seven seems to be a magic number for people’s comprehension: people generally cannot keep track of more than about seven chunks of information at once.
Not surprisingly, the Clean Code book adopts a more aggressive stance, asserting that three arguments should be avoided whenever possible and more than three requires very special justification.
This rough upper bound of three parameters is closer to how I feel. Around four parameters, I start to feel uncomfortable, and my feelings get increasingly worse when I reach five parameters or more.
Five parameters or more may seem uncommon to you, but plenty of codebases are littered with functions that have six, seven, and even more. This is unfortunate because long lists of parameters severely degrade software quality.
Raising awareness of the problems of long lists of parameters, and showing how we can shrink these lists, is the main motivation of this post.
Hopefully, next time you come across a long list of parameters, you will feel motivated to refactor the code towards smaller, more cohesive, and higher-quality functions.
1. Problems of long lists of parameters
Long lists of parameters negatively affect three desirable properties of software: readability, maintainability, and testability.
When a function has many parameters, reading its signature or a call to the function requires a lot of mental effort. And, as the number of parameters grows, mental effort increases exponentially.
Any time you need to switch to full-concentration mode to understand individual statements, reading code becomes a pain. Statements should be so straightforward that they glide through your eye. It is only when statements are so trivially simple that reading code becomes a pleasure.
Which of the following statements is easier to understand?
An important problem with function parameters is that, whenever they change, the callers of the function must be updated. This will happen more often as the number of parameters grows larger.
Also, the more parameters a function has, the more information the callers need to know to invoke it. This increases the coupling between the function and its callers.
The higher the number parameters and the tighter the coupling, the bigger the pain to maintain the function.
To prove that a function works, you would have to write tests for every conceivable input.
For any non-trivial function, this is impossible. You must choose tests wisely.
Choosing tests wisely is easy when a function has 0 or 1 parameters, but it becomes increasing harder with every new parameter because more combinations come into play.
Functions that have many parameters are extremely hard to test.
2. Refactoring towards better design
To reduce the number of parameters of a function, you have several refactoring alternatives:
Create a new data structure
The parameters of a function often share some kind of logical cohesion. If this is the case, and you find that the set of parameters are consistently used together, it may be wise to group them in a new data structure or class.
This has the additional benefit of making the relationship between the parameters more explicit and the code more intent-revealing.
You can also prevent primitive obsession.
Join several functions into a class
If you are consistently passing the same parameters to different functions, you can group the functions into a class. The parameters will become data members of this class, and, therefore, the functions will be able to access them directly (without requiring input parameters).
In a similar way to the previous solution (creating a new data structure), this solution applies when the new class represents a useful abstraction that deserves its own name.
Avoid "what-to-do" parameters
A boolean parameter often indicates that a function does two different things: one for
true and another one for
This can easily be generalized to other parameter types, such as integers and strings.
Regardless of their type, we must avoid "what-to-do" parameters that are passed into a function only to select from outside the internal behavior of the function.
This breaks encapsulation.
When a function has several behaviors, the best thing to do is to split the function into smaller functions that do one thing and have less parameters.
Avoid output parameters
The natural interpretation of parameters is as inputs to the function. This is why input parameters are easier to understand than output parameters.
Whenever you are reading code and you wonder whether a parameter is an output parameter, you are breaking your reading flow.
This should be avoided.
If possible, you can reduce the number of parameters of a function by keeping only the inputs. If you need to change the state of something, it can be the state of the object that owns the function.
To end, a short disclaimer …
In our lives in general, and in software development in particular, almost everything depends on context. Long parameter lists are no exception. They are more accepted in some domains than they are in others.
Therefore, it is always good to keep in mind that, every time we invest a non-trivial amount of time refactoring code, we should make sure that we are solving a real problem.
As far as my experience is concerned, I have always found that short lists of parameters make my life easier. This is why I believe that applying the refactoring techniques from this article is often a good investment.
Great article. Just wanted to add that in some languages functions can only accept 1 parameter and the advantage of that is that one is then able to take advantage of Combinator patterns. Combinator patterns are extremely powerful and promote decoupling at the code level.