DEV Community

Sergiu Mureşan
Sergiu Mureşan

Posted on

When do you create a function or method?

It comes naturally to create a function whenever needed, that should be true for all developers. But we all have different standards on when we create methods.

Are you creating a function/method when:

  • the current one exceeds a certain number of lines, or a certain complexity?
  • or do you just pile all the code in one giant function until you can't see the scrollbar?
  • do you create them based on readability?
  • what about re-usability?

Leave your answers below! It would be very interesting to see the differences we have on this subject.

Top comments (61)

Collapse
 
bgadrian profile image
Adrian B.G.

I don't know how to put it in words, it "feels natural" after a while, I guess there are some general guidelines:

  • 1 function should do 1 thing (when you add the 2nd one you extract it)
  • threshold of around 20LOC (it depends)
  • when you have multiple nested loops
  • when you have a switch case with more then 3-6LOC
  • when you want to reuse a small functionality (within the same module)
  • when you cannot easily understand what the function does (smelly code)
  • when you have a not-so-popular algorithm or math formula
  • when you suspect a part of the function and you want to test that

Note: These apply to procedural and OOP code.

Collapse
 
codevault profile image
Sergiu Mureşan

Haven't thought of the math formula use case. One more thing to add to the list.

when you cannot easily understand what the function does (smelly code)

Do you look into the inner functions of the one that you are analyzing to see how hard it is to understand as whole? Or even better, do you look at the places where it has been used to see if it makes sense and it's not too difficult to understand just from the call?

Collapse
 
bgadrian profile image
Adrian B.G.

I don't know really, I just follow some common sense rules, and usually I put myself in a junior dev shoes, would he understand this?

I was referring to inner functionalities like a sort before a search, or a normalization of a json before working with it. If that part is too ... messy I would extract it in a private function, or lambda or something to put a name on it. Like you rename a variable, I would encapsulate the piece of code.

Thread Thread
 
codevault profile image
Sergiu Mureşan

Wouldn't it be better to just comment that code block and tell the reader what it does?

Thread Thread
 
bgadrian profile image
Adrian B.G.

I don't know, it depends, we'll see when we get there :D

If that piece of code has also 3 temporary variables and some loops, a comment would not suffice, if is a one liner maybe.

Comments can be tricky because they usually remain outdated.

Thread Thread
 
codevault profile image
Sergiu Mureşan

Totally agree with that last line, you are right, most of the time they do and they are really confusing.

I am mostly against comments in such cases but, creating another private function might complicate the problem even further... I think creating functions that have almost no reusability is a problem.

Although, the local lambda could be the best option there. The biggest problem is the way you read it. You have to over the lambda code until it is being used and then come back to it. Can be quite tricky.

Thread Thread
 
bgadrian profile image
Adrian B.G.

Reusability is a rare reason for creating new functions, most of them are called for one place and used to separate the concerns and have small functions, so a lower cognitive load.

Thread Thread
 
codevault profile image
Sergiu Mureşan

Reusability is the primary reason I create functions. Interesting to see a completely different view.

How do you deal with such complexity? I mean, from what you told me, you can have tons of functions that are hard to understand on their own. Isn't that a problem?

Thread Thread
 
bgadrian profile image
Adrian B.G. • Edited

I think you are taking everything I say to an extreme, without a concrete example there are just guidelines.

You can read some open source code and see that re usability is not usually a concern for new functions, most would prefer a WET code than a 200LOC function.

Here are a few random examples:
cleanGlobPath function is a private function just to take the "load" off the Glob public function.
doubleCapacity function is a similar example, it can fit in the addFunction but is better to extract it.


Another reason I forgot about is when you want to protect a functionality to be overridden by an ancestor you can move it to a private function and let the function that can be overridden empty, just a proxy for the private one.

Thread Thread
 
codevault profile image
Sergiu Mureşan

Alright. I understand now. Maybe I'm just too used to weakly typed languages. Thanks for the great examples!

Thread Thread
 
bgadrian profile image
Adrian B.G.

Yes unfortunately in JS there is a preference to use lambda/ functions inside a public function, instead of declaring them outside, mainly because of the lack of language features (private functions ando/or modules support), but hopefully things will change in the next few years.

Anyway I don't normally bring JS in discussions about good/clean code because I haven't found yet a project or library that has one (in my oppinion). Even in large common libraries is custom to have 2-3 nested functions, 100LOC functions (random example Vue, createComponent function does at least 3-4 things, instead of 1), acute lack of defensive programming and other "bad practices".
PS: If you want to have nightmares go trough the NPM source.

Thread Thread
 
codevault profile image
Sergiu Mureşan

I don't think large functions by themselves are really a problem. Large function that do way too many things without any structure or good flow could easily become a problem though.

Also, yeah, that createComponent function is difficult to understand... but maybe because I don't know much about VueJS

Collapse
 
revskill10 profile image
Truong Hoang Dung • Edited

Please always use function as possible, and outsource the this.
I mean, instead of

class AwesomeClass

  def hello_world
    this.variable = other_method();
  end

end

use this


def hello_world(this)
  this.variable = other_method();
  return this;
end
Collapse
 
bbasile profile image
Basile B.

Yes this is based on the fact that member functions should only be added if they use private or protected declarations. Member functions that use only the public declarations of an aggregate are just bloating the aggregate. This can even, in some rare cases, have a performance penalty, for example if the language has virtual by default (yes this exists !) however most of the time it's just a matter of readability (especially for languages that don't separate the declarations from the implementation).

Collapse
 
codevault profile image
Sergiu Mureşan

Which language is this?

Collapse
 
revskill10 profile image
Truong Hoang Dung

It's just a pseudocode based on Ruby.

Thread Thread
 
codevault profile image
Sergiu Mureşan

Interesting. Few languages have this feature. So does that make the function an actual method but outside the class?

Thread Thread
 
revskill10 profile image
Truong Hoang Dung

I edited by replacing function with def keyword.
Basically, instead of making class as global, use more functions as global primitives.

Thread Thread
 
codevault profile image
Sergiu Mureşan

And where do you put these functions?

Thread Thread
 
revskill10 profile image
Truong Hoang Dung

Put those right in Ruby files. Then when you require that file, you can use your functions.

Thread Thread
 
codevault profile image
Sergiu Mureşan

So just one extra file where you put all functions (or methods in this case) related to that class?

Thread Thread
 
revskill10 profile image
Truong Hoang Dung

my_modules.rb

def function1
end

def function2
end
Collapse
 
tiso profile image
tiso

My primary goals are:

  • readability - by being so expressive as possible and use short methods
  • hiding details - by using private methods for them
  • avoiding duplicities - by moving that code to reusable method
Collapse
 
codevault profile image
Sergiu Mureşan

That's a great list so far.

What do you think about maintainability? Should you be reducing the amount of methods or functions you create to help maintainability?

Collapse
 
tiso profile image
tiso

You mean 10 longer methods instead of 20 shorter? Or something else?

Thread Thread
 
codevault profile image
Sergiu Mureşan

Yes, too many can become a headache when trying to find if a functionality has already been implemented or simply when trying to debug a function, it's quite difficult to retain a lot of function calls.

Thread Thread
 
tiso profile image
tiso

It's really hard to tell. It's more like a process from less and longer methods (and less classes) to more and shorter, until its feels right.

How to find what was implemented? Put things on right place. So proper domain modelling, group helper methods to helper classes

I often think about methods in layers - public method is first layer. Do I need to check what called private methods do? Not so often.

Collapse
 
lexplt profile image
Alexandre Plt

Hi!

I am creating functions for small and independent tasks, which do not need a context (eg removing characters from string), and methods (and their classes) when it's related to a more complex object (eg an array of Block representing a map in a game) and it needs to use a specific context.

To me, methods are better than functions when you need to give more than 4 or 5 arguments. In this situation, it means you need to create a class to handle those arguments (often, there isn't only one function doing stuff with those 4-5 (or more) arguments).

Collapse
 
codevault profile image
Sergiu Mureşan

Great answer.

I have a question. If the method has 4-5 arguments and all can be modified then in which class do you put it? The most important argument?

Collapse
 
lexplt profile image
Alexandre Plt

I would create an abstract class using those arguments as attributes, if they are used in more than 1 function, to have all the data processing in the same place.

Usually, I choose a name related with how I play with those data and not regarding the most important argument.

Thread Thread
 
codevault profile image
Sergiu Mureşan

This would work most of the time but wouldn't that create too many classes if there are many such methods?

Thread Thread
 
lexplt profile image
Alexandre Plt

It could, but if you can "group" methods playing with the sames arguments (but doing different things with those) it can be pretty efficient in reducing SLOC (physical lines of code) and code complexity (IMO, in some cases creating a function with more than 4-5 arguments is better than creating a class).

To me, the most interesting thing with a class is that it handle its resources on its own, all of them, in the same place.

Thread Thread
 
codevault profile image
Sergiu Mureşan

Yup, that's why they were designed. Data with functionality. Just be careful not to group data with functionalities that don't actually deal with the data as whole but just parts of it. Or even worse, consider functionalities as objects and creating classes based on those. It can lead to so many headaches.

Collapse
 
cathodion profile image
Dustin King

For me, it's about readability and DRYness (DRY = Don't Repeat Yourself).

It doesn't always have to be something that will be called in more than one place, but wherever it would help to give a piece of functionality a name, even if it's one short line of functionality, it might be time for a function.

On the other hand, too many small functions could inhibit readability too. It depends on the code I'm working with.

Collapse
 
codevault profile image
Sergiu Mureşan

Great response!

Personally, I don't see repeating yourself that much of a problem. Sure, if there are 5-10 lines of code repeated across your code you should create a function and use that, but I also encountered the opposite, introducing problems by creating functions and using them everywhere.

The problem was the function itself. It was fairly short (less than 5 lines of code) and it was almost certain that it will be modified in the future, but not for all calls, only for some. That's where creating a function can bite you.

Collapse
 
iamkalai profile image
Kalaiarasan Pushpanathan

I try to stick to the concept of 'one function - one task'. If the function needs to perform some processing before performing the task, the pre-processing goes to a different task.

Collapse
 
codevault profile image
Sergiu Mureşan

Isn't preprocessing of a task part of the task itself? Do you take into consideration the reusability of a function?

Collapse
 
iamkalai profile image
Kalaiarasan Pushpanathan

Again depends. Let's say you want to validate the data or format the data in the form the function accepts before processing it, you can move that logic to a function.

Thread Thread
 
codevault profile image
Sergiu Mureşan

Interesting. A concern I have is how do you deal with all the parameters? Does the preprocessing function modifies all its parameters?

Thread Thread
 
iamkalai profile image
Kalaiarasan Pushpanathan

No. It does not need to. I just gave preprocessing as a example.

Let's just assume that the function accepts a parameter. Based on certain condition, it either needs to insert a record or update the existing one.

You can split up the logic of insert and update as two separate function. Here the main function almost acts as a wrapper. The main business logic resides in two different function, which can be reused elsewhere as required.

Hope that helps.

Thread Thread
 
codevault profile image
Sergiu Mureşan

Thank you, that makes much more sense now.

Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

I create functions whenever I can. My main concern is readability. Testability is entirely secondary, since a lot of my functions will remain private, and not directly tested.

Some key indicators you need a new function:

  • You have a one-line comment, it's usually better as a function with good name
  • You have blocks of code separate by whitespace in a function. Chances are, those blocks do something logical, thus can be separated.
  • You need to scroll up to the function arguments. Long functions are hard to reason with.

There are several exceptions. If adding a function would make the code more complex, then I tend not to do it. I also use a lot of lambda functions lately, grouping behaviour locally and making use of the same scope.

Collapse
 
codevault profile image
Sergiu Mureşan

You have a one-line comment, it's usually better as a function with good name

Are comments a rarity in your projects? Also, do you work on a team or alone?

Collapse
 
georgeoffley profile image
George Offley

I create a function whenever I need to repeat code. Other than that, why tempt fate with an inheritance error.

Collapse
 
codevault profile image
Sergiu Mureşan

That's the first time I heard the term inheritance error. Do you mean you will not create any classes?

Collapse
 
georgeoffley profile image
George Offley

No, I create them out of habit anyway. However, consciously I know I need to create one whenever I need something that will repeat itself somewhere else.

I also said inheritance error when I think I meant scope error.

Collapse
 
andrewlucker profile image
Andrew Lucker

I create methods if they make sense as a verb:

"send the file to the user when they specify the file to be sent" send, specify, when? stuff like that.

FactoryFactoryRefactorFactoryDetractor... no.

Collapse
 
codevault profile image
Sergiu Mureşan

Interesting take on it. I always think you should start the name of the function with a verb but didn't take it that far. It does sound reasonable.

I'm sure there was a post about Factory/Manager/Something-er classes on here... can't find it at the moment.

Collapse
 
pranay_rauthu profile image
pranay rauthu

whatever it is, just give it a proper name.