6

What does it mean when you say that a method or a function should do only one thing?

Can it do a few things as long as it has a cohesive name?

Should we avoid routines with the word "and" in them?

Please provide concrete examples.

Note: please don't confuse this question with the Single Responsibility Principle which is about classes and does NOT mean "classes should do one thing only".

5
  • 1
    @Telastyn If it does, that's accidental; the single responsibility principle applies to classes, not methods.
    – Piovezan
    Dec 26, 2019 at 20:58
  • 2
    I could track down the other dozen questions about this... But I also think that the single responsibility principle applies perfectly well to functions.
    – Telastyn
    Dec 26, 2019 at 21:02
  • @Telastyn Thanks, maybe it does but I find 'Having small, focused functions is good' too sujective.
    – Piovezan
    Dec 26, 2019 at 21:07
  • "Having small, focused functions is good" has been a well-known and accepted guideline for almost as long as there have been functions. And it's something that's been documented as such plenty of other places on the internet. How do those resources not answer your question?
    – Telastyn
    Dec 26, 2019 at 21:12
  • @Telastyn because I'm looking for concrete examples (sorry, I hadn't added this to the question).
    – Piovezan
    Dec 26, 2019 at 21:15

3 Answers 3

19

If I tell you to "take out the trash" I've asked you to do one thing. It's one cohesive idea. Does that mean it can't be decomposed into more things? No.

Everything can be decomposed into more things. If I tell you this function should do one thing I'm not telling you it should manipulate only one trash bag or only one quantum quark. I'm telling you if a function named "take out the trash" also implements "bring in the mail" it's going to surprise and annoy me.

Now you can argue that it makes sense to bring in the mail when you take out the trash. That's fine. Where you went wrong was the name.

"Do your chores" fits, but it's jarring. It's a grab bag that doesn't tell you exactly what is inside. But it does tell you what should and shouldn't be inside the function. It's different than before because this is a higher level of abstraction. That's why this is still one thing.

You could argue that it's doing two things. But I could argue that taking out the trash involves walking, tying up bags, and opening doors. There is no thing that is one thing. But there are things. Clear things. Things that say these things don't belong in this thing. Such things have cohesive names. Names are exactly the limiting factor here.

"Deal with trash and mail" is bad mostly because it does tell you exactly what is inside. It's telling you implementation details. It has not taken any responsibility for communicating the single thing it exists for. It's just telling you what it contains.

Given this what should you find when you look inside "do your chores"?

If it's a list of containers to open, items to remove, and locations to carry them to then you've wasted two good names and a useful level of abstraction. Please don't throw away "take out the trash" and "bring in the mail" just because "do your chores" exists.

If you want to follow "Do one thing" step back from what the function does and ask what it's for. What does someone calling it really need to know? What details don't they need to know? Can you name it something that will mean something to them?

That's the one thing. Sure you can cram every detail into the name but it's better if the name centered around one single idea of what the function is about. Not every little thing it does.

2
  • 1
    Of course, "take out the trash" and "bring in the mail" as the two separate and isolated parts of "do your chores" also perfectly illustrate the cost of abstractions: It removes chances to take advantage of the nitty-gritty details to eliminate wasted work, in this case duplicate running-around. Does the loss of efficiency matter? Only if the numbers say so. Dec 27, 2019 at 2:32
  • Deduplicator true. But that 'wasted work' is clear and obvious. Thus if optimization is required later you know where to find it. Also, if trash day and mail delivery day become different days it's not hard to tease them apart. Dec 27, 2019 at 2:36
2

Indeed, the Single Responsibility Principle refers to each "class" (a cohesive unit of code, if you like, an object) to have only one reason to change, not to do only one thing.

The key issue (the usual suspect) is productivity. It is the same reason why we prefer to:

  • Not have mixed definitions in a dictionary (e.g. a "lion-and-bird" definition).
  • Not organize folders by multiple-types-of-documents (e.g. a "phone-bill-and-tax-documents" folder).
  • Not mix Beatles and Everly Brothers in the same Audio CD (...ok, not the best example, but it doesn't help having to check all contents of an Audio CD to see if it contains the band you like, a single band saves time).

Because it makes us less productive. We have to look deeper to find what we want, and we are impatient beings because time is money!

We like to organize things and a function that does multiple things goes against productivity. It doesn't matter if it lowers productivity by 0.1% now. Productivity loss accumulates and we are often perfect victims of the it-seems-clear-to-me-at-the-moment fallacy. The few seconds it takes you today to understand what goes on easily become minutes in a couple of months. And minutes easily become hours when you have multiple such "multi-modal" methods to manage. And it can get worse... They are not always methods you have written. And you may not always have the original method composers at hand to give you explanations.

Regardless of your skills, you are more productive handling and maintaining simple functions. Even the best and most cohesive name in the world would still leave you having to read the entire name of the function and then skim through the function to find out the part you want to maintain (check, review, debug, etc.).

In short, you can have a method do as many things as you want it to and nobody keeps you from using "and" in the name, appropriately, of course. But after you do this a few times in a relatively large code-base, it is only going to be a matter of time before you wish you hadn't!

1
  • I find opportunities of adding "and" to a method name quite often, I guess this is a smell to be wary of. And this answer differentiates from the accepted one by embracing "check the function's contents" instead of glancing at the abstraction. I think it is sometimes necessary to inspect the method/function contents. +1
    – Piovezan
    Jan 25, 2020 at 18:06
1

What does it mean when you say that a method or a function should do only one thing?

It means that a method or function should only do one thing...

Once it does two things, it becomes more difficult to test (since you can't test those two things in isolation). Once it does two things, your code is often more fragile (since people often assume that both things always happen, rather than a failure happening between them). Once it does two things, your code is less flexible (since you'll very often need to reuse one of the things without the other). And that ignores the harm to debuggability, naming, maintainability, scaleability and all of the other qualitative aspects of software and the code in general.

"only do one thing" is a guideline to help all of these things that we care about. It's not a rule you must follow.

Because pedantically, the overwhelming majority of functions do more than one thing. "Add these numbers and return them" is two things. "Check this input and save it to the database" is two things. What we really care about is coupling.

If your function takes two smaller, components and combines them to do something slightly more complicated, that's fine. The smaller bits can be tested/reused/understood in isolation because they're decoupled outside of this function. The "one thing" it does here is combine the two smaller components. But the key thing is that we don't really care if it's "only doing one thing" - we care if the code is readable, testable, scaleable, etc.

Can it do a few things as long as it has a cohesive name?

A cohesive name is a hint that it could be a reasonable abstraction, but is no guarantee.

Should we avoid routines with the word "and" in them?

Likewise, a FooAndBar function is a smell that your abstraction isn't granular enough, but is no guarantee.

Not the answer you're looking for? Browse other questions tagged or ask your own question.