Day 18 - Investigate long parameter lists

Do a search through your project for long parameter lists, where long is defined as more than three parameters.

Due to long parameter lists often being line-wrapped, you’ll likely have to do some manual searching, but here’s a naive regex that will find single-line method calls (in Ruby, at least) with at least four parameters:

(.*,.*,.*,.*)

You’ll probably find that many of your long parameter lists appear when calling library or framework code.

However, you might find some in your own code. If you do, it doesn’t necessarily mean there’s anything wrong, but it’s worth asking yourself a few questions about them:

  1. Should any of the data that you’re passing in be instance data instead? A clear indication this is true is if other methods on this object also require the same parameter.
  2. Do you frequently pass several of these parameters together? If so, it’s possible you have a Data Clump and could benefit from extracting a value object to contain them.
  3. Are any of these parameters booleans? If so, you probably have a case of control coupling , and would do well to remove it.
  4. Can any of these parameters be removed outright? You’d be surprised how easy it is to continue passing something into a method that no longer requires it.

By the way, the above questions are worth asking about just about any list of parameters, so consider them even if you tend to keep your parameter lists short.

Good luck!

Such a simple tip and yet something I never consciously think about. Improved a couple functions and definitely making them better readable.

I quickly found parameters that most likely can go together: start_date, end_date, period which obviously can be represented as a value object.
Thanks for the tip!

1 Like

I realized some of the test code could be improved. We were passing down 4 arguments, 2 of which could be inferred if we simply passed down the source object instead.

Good morning!

I ripgrepped through my codebase, and the only long-ish parameter list there were in Rails helpers. I can live with that though, since those exist literally to compose a string anyway, so factoring out a value object doesn’t make too much sense :man_shrugging:

2 Likes

I took the chance to clean up the way we configure attributed strings. We had the same list of 6 arguments being used a couple of places. These have now been turned into a separate type with default values.

Ran a check in one project with IntelliJ to find signatures with more than three arguments. The only places are “main” to kick the whole app off and three framework calls we can’t change :+1: Was happy to see we adhered to a small parameter list already!

1 Like

Same as @julianrubisch, only a few in Rails helpers. And of those many of them are really just two required args with an opts (which gets passed along to framework methods), or &block. Happy enough with them as they are.

1 Like

Adjusted this re with def .* to get the definitions of methods that would like to receive a lot of arguments, and found that we’re mostly good. Most exceptions were for factory-like methods that allowed to override the default values we mostly use

Found a data clump similar to @andrewG with a start_date and end_date that could be represented as a ruby Range period. However these were parameters of an ActiveJob perform method, and at the time of writing its supported parameters don’t include ranges. Maybe a good idea for a future PR to Rails.

1 Like

Detecting too many arguments in python code

Install wemake-python-styleguide which is actually flake8 plugin.

It can detect many issues, but focusing on too many arguments in a call for module distribution_platform:

$ flake8 --max-arguments 3 distribution_platform
distribution_platform/crud.py:10:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/crud.py:26:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/crud.py:37:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/crud.py:63:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/crud.py:79:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/crud.py:93:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/crud.py:119:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/crud.py:134:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/crud.py:148:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/crud.py:183:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/crud.py:229:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/crud.py:275:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/crud.py:316:5: WPS211 Found too many arguments: 4 > 3
distribution_platform/api/endpoints/feed.py:41:1: WPS211 Found too many arguments: 4 > 3

This works perfectly even for function call spanning multiple lines.

How much is too many

Regarding number of arguments in a function call I heard a rule do not use more than 5 arguments with very simple explanation: Human brain is good at focusing on up to 7 items (that is why jugglers with more than 7 balls are so special).
And as the programmer must also pay attention to some contextual information, leave 2 things for context what makes up to 5 arguments for a call.

max 3 arguments seems too few to me.

On the other hand - all the cases having more than 3 arguments share the same set of arguments and I am going to rewrite it to a class (“user resource”) having some methods on it.

Thanks for inspiration.

Great tip!
New stuff for me. Learned about data clump, control coupling. I’ve also identified one spot in my personal project containing more than three parameters.

I agree, great tip. Just last week I wrapped accessKey and secretKey into a Credentials class, totally worth it in my eyes. :slight_smile:

This has been very true. I spent the whole 20 minutes just looking at these.

It can be hard to improve these in 20 minutes. We would need some kind of Adapter class, which can be good for isolating dependencies.

My co-worker found that we frequently need a start_date and end_date. It would be nice to have a class like:

class DateRange
  attr_reader :start_at, :end_at

  def initialize(start_at:, end_at: Time.current)
    @start_at = start_at.to_datetime
    @end_at = end_at.to_datetime
  end
end

So that we could turn some of these:

start_date = 2.months.ago
end_date = Time.current
some_func(start_date, end_date)

Into these:

date_range = DateRange.new(start_date: 2.months.ago)
some_func(date_range)

Of course, this needs more thought but it’s definitely good to keep these examples on the radar.

1 Like

My personal limit is 27 parameters being passed, luckily most of my functions take between 15 and 20, so no real work to do here.
:slight_smile:

The above is intended to cause panic for my colleagues if they read this.

Changed some parameters to email templates and infer more information from objects that are being passed instead of all the parameters.

Still going with this as I want to complete the challenge even though it’s finished :smiley:

With this one I spent most of the 20 minutes finding and running the right tool finding long argument lengths and there was one obvious refactor which I carried out, there are others but they might open up a bigger refactor which I don’t have time for right now!