Before I started writing this post I searched for other people looking at code
code from this perspective - found one How deep is your code?
I needed an example of complicated Service Object and
found it in one of the Discourse source files. I’m not criticizing
this code by any mean and there is a great example of what I’m going
to try to achieve in the same repository
I personally prefer small classes, short, single purpose methods over
the one big piece of code. Having said that I have to admit that this approach
has a significant downside - methods are short quite often
because internally they call other methods, which call other methods,
which call other methods… So you have to go down the rabbit hole
before you figure out what method actually does, sometimes you loose the
big picture in the meantime.
I don’t want to do this, I don’t want to look at 5-10 private methods to figure
out what are the implications of calling a public method, I want public
method to be able to tell me the story right away, the very first time I
see it - maybe there is a way?
Once I saw a complexity metric described by Sandi Metz in her talk All the Little Things - the Squint Test.
While this metric was used to measure the complexity of the nested conditionals, we
could think about calling methods in a similar way. If a method calls other
method let’s indent it as if it was a nested if:
now let’s write down all involved methods and indent each nested call
we got similar figure with changes in shape, the bigger is the shape
change - the more nesting we have.
I’ll skip details here because those are quite straightforward extractions, they were done to simplify
UserUpdater#update method, make it small and easy to understand.
However I believe that too much information was hidden with this
extraction. From the method definition we no longer see that:
There is a transaction
Some parts are updated only if particular conditions are met
We use constant to determine what to update
It’s not obvious that we return (and depend on the returned value
somewhere) true/false depending on the
result of the transaction
In addition to hiding information, I strongly dislike how new
UserUpdater#update_user_profile method looks - it’s a private method that
consists of only other private methods, and once again - no conditionals
inside
I believe that methods like this only introduce additional level of
complexity and have no right to exist
In this step we drop private methods that were (almost) only wrappers
around other private methods and change remaining method names to
reflect the intention better
the UserUpdater#update has become almost three times bigger compared to
the previous version however it now also tells us a better story.
It’s not perfect (and will never be) but we will make one more step to
improve it and compare with the shorter version from Step #2 afterwards.