26 comments

[ 3.2 ms ] story [ 70.7 ms ] thread
I like the small function practice. However, I'd like a better to organize my code or be able to hide the extra small functions so that they are not visible when I'm scanning a file to get an overview. I could place them at the bottom of the file but I prefer to keep them close to where they're called.
> "I'd like a better to organize my code or be able to hide the extra small functions so that they are not visible when I'm scanning a file to get an overview."

Most of the 'extra small functions' make good reusable utility functions from which you can build up an external utils lib with different categories for different functions to keep your main code clean. Reasoning about those 'extra small functions' makes you also become more aware of useful third-party code, like standard libraries and popular utility libraries. So most of the 'extra small functions' can go into an external lib and the rest goes into 'helpers' or alike. This way you can keep the files which contain the app logic clean and readable.

I think the thing that really determines whether I break something into a separate function is state. The more state I have to pass into my function, the more useful that function has to be to justify its existence.

A good name also helps a lot.

I think refactoring to create a new class is a good solution to the state problem. That is, if you have code like this:

    func drive(Engine e, Destination d)
    func park(Engine e)
    func start(Engine e)
You can create a new class

    class Car {
        constructor Car(Engine engine)
        func drive(Destination d)
        func park()
        func start()
    } 
Which accepts the shared argument in the constructor. That way within the Car, you should have minimal state, and defining new functions can reuse the shared argument that the Car was constructed with: no more long argument lists.

A Car was sort of an obvious example but applying that pattern allows me to see less obvious examples, where a class is some abstract entity that I wasn't aware of before.

I think there's still fear of undisciplined state management in large classes, but that can be addressed with good SOLID class organization.

Multiple shared parameters up is a strong motivation to create a composite type, yes, but one should not instantly jump to making the functions methods of the class. In your example it makes sense, but there are equally many cases it does not. Shared state and shared behaviour are two entirely independent concerns.
Yes, I was responding to what I assumed was the parent's concern that passing state into a function was a hassle. One of the sources which influenced my tastes is Robert Martin's Clean Code.
I agree with all of this if the programmer understands when to break off these functions into a object or abstraction. Other wise you are trying to figure out how to create a car object and turn it on while passing by all the engine functions that are parts of the implementation you simply don't care about. I'm starting to think compiled languages have something else going for them above interpreted languages as I start to miss reading and writing headers for how to use my class.
This article is absolute humdrum. Functions are not the atoms of programs. We now have classes, lambdas, generators, events, streams, etc.

There are ways to organize code without sticking every little one time used code into a function.

Poor article by a pseudo computer scientist. Let him keep 'hacking.'

I don't recall the article insinuating that classes/lambdas/etc don't exist, only that you can break up long functions into smaller functions. Also these functions are already presumably within a class, since Martin Fowler writes a lot about Java.
Garrett Smith did a talk on Writing Quality Code in Erlang[1] which highlights this priciple of separating intention from implementation. He starts with a 20 line function and refactors it into 16 separate functions. The result is much more verbose but it's much easier to read, understand and change. I think terseness rarely does programmers any favours in the long-run.

[1] https://www.youtube.com/watch?v=CQyt9Vlkbis

It would be interesting to see an IDE that displayed tiny functions inline (collapsable) in cases like this. This way I get both worlds. Small functions, but the ability to read the program flow straight through.
I don't agree entirely with what the article says because it can be a problem to have to sift though a lot of small functions in order to determine what is going on. Sometimes, I think it's easier to read a moderately sized, but "clean" function that you can read from top to bottom, rather than a spiders web of references to other functions which may contain the nuance or bug you are looking for. What he has said reminds me of what the Forth community necessarily espouse, though.

People tend to take the length guidelines far too seriously though. Instead it requires thought and experience. The main Principe I try to follow is make it readable, it doesn't matter how it's written or if you've decided to use a goto in there, so long as it is one of the more readable alternatives.

There is a less chance to have a bug in a very small function than moderately sized, but "clean" function.

It is also easier to write a test for a smaller function.

Having the implementation in many smaller functions also makes the code more readable if their naming is good.

> There is a less chance to have a bug in a very small function than moderately sized, but "clean" function.

Maybe so. But you need several of the very small functions to do the same work as the moderately sized function, and those several very small functions have to interact with each other. So did the total chance of a bug go up or down? It's not clear, but I lean toward the one moderately-sized function being more likely to be bug-free.

I think realistically in many cases the buggedness will still favour many smaller functions - if for no other reason than they must all be named.

The higher level function is then easy to read. The lower level functions get a name that can be checked more easily against an implementation if needed.

readability is just one small piece of the reason why keep one function does one thing. Understanding how to make your code SOLID is very important.

After all, software development is still a engineering process, in which personal coding style is far less important.

I like the takeaway on reasonability. I've heard mantras on "there's too many lines of code here, break it up!" but if it reads well on its own then no worries...

Break things up for reasonability. If there's a bunch happening within a function body and you can give some semantic meaning to what's going on in there then go for it.

Once I thought a function should fit on a screen. I did small functions and tried to use the function names as comment replacement.

But after coming back to old code for years now, I think one big function that can simply read from top to bottom without any jumps is the way to go.

Hmm. When API naming is good, you shouldn't need to be reading at the low level much, so IMO whatever makes it easy to skim for maintenance is the way to go.
IMO people are mistaking the map for the territory. "Function length" is actually a proxy for the real things you want to improve, like:

(A) Simplified flows of data in/out rather than a huge complicated input and a huge complicated output complicated parameter/return data flow

(B) Reusability, since bigger functions are more likely to be "custom fitted" to a particular purpose.

If your huge function has very simple parameters/returns, and no subparts you want to reuse, then odds are there's no need to break it apart.

It's a lot more than these two things, in fact I think these two things are not as important as the main benefit of smaller functions, which is making code that is easy to understand at a glance. Good naming with proper separation of logic into small pieces can make even c++ read almost like prose. It's hard to find, but when you see it, it always blows me over.
While I do not disagree with Fowler's criterion as being one of several to consider, The ability of function (and identifier) naming to make code "easy to understand at a glance" is frequently overstated, including in this article. Once code reaches a size where it is moderately complicated, the reasons for some action being done in a particular way are not necessarily locally obvious (or obvious at all.) One common reason for this is that things are being done in a particular way to conform to a contract that is relied upon in multiple, disparate parts of the program (and any reasonably large program does have such contracts, even if it was not produced by an explicitly design-by-contract methodology.)

For a flavor of how this works, read the article [1] below, in response to Tom Cargill's classic article 'Exception Handling: A False Sense of Security'. Even though the code example here is small enough to be understood in one go, I doubt that the reasons for it being the way it is are immediately obvious to most programmers, unless they have been primed by exposure to the issue before (note that the first answer given was not entirely correct.)

[1] http://www.gotw.ca/gotw/008.htm

I like this line of thought and to an extent, I agree with it pretty strongly.

I also think that if you are adhering to the notion of a "single level of abstraction" per function, then you will naturally need to structure your functions in the way Fowler recommends.

I once had to work on someone else's function that was several thousand lines long, written in 'C'. Some of the code blocks went on for pages. The code had lots of loops and conditionals. The code was nearly impossible to understand. My first instinct at trying to understand something like this is to break it down into pieces that I can understand. I like to pull out the body of for loops. for loops often represent either map(fun, sequence) or filter(fun, sequence) or fold(fun, sequence). More generally though, I just look for large code blocks, those delimited by {} and pull those out. If one is familiar with functional programming, one may recognize the block as some common function, maybe a partition or a grouping. Functional languages are very good at abstracting away common patterns. I like to look at a block of code and see if it matches one of these patterns. With the above large function, pulling anything out was a huge chore. This was a time before refactoring tools. This large function had tons of variables, all declared at the top. The scope of these variables was huge. Pulling out a block requires an analysis of what goes into the block and comes out of the block. What goes in, is not so hard, and you can use the compiler to help. What comes out on the other hand was near impossible. You need to determine if code below the block uses a variable set inside the block. In a smallish function, no problem, but in the one I was dealing with, near impossible. Essentially, you had to pass them all out. Only after tons of further refactoring of pulling out code blocks could one begin to see whether variables needed to be return values or were simply local temp variables. By pulling out a block from a large function, one gains a function that has defined input, defined output, a smaller state space of variables , a smaller amount of code ranging over the variables, a name, maybe a comment, and maybe some tests. This function is much more easily understood in this context than it is as code block in a large function. This large function showed me in great detail how horrible they truly are. Breaking the large function into many small ones was the only way to actually understand it. It turns out that the huge number of local variables were really a union of many smaller sets. One set may have been completely local to a code block. What became obvious to me is a kind of complexity measure. The more variables and the more code ranging over these variables the higher the complexity. I believe there to be a sort of exponential growth in complexity when adding variables and code length. When broken down sufficiently, the complexity of each function can become nearly trivial. One then needs to analyze how a bunch of trivial functions combine to make the whole. This is a much simpler task than analyzing the whole when it is one function. I can't prove it, but that is my experience. I think if one does a little study of a library in a functional programming language, like Haskell or LISP or Clojure to get a feel for what makes for a good abstraction, and ever has to deal with a hugely large function, I don't see how you could not come to the same conclusion that smaller is better. Can a function be too small? Well, I like this discussion of intent. Another piece of code that I once looked at had a fairly complicated expression and involved some bit testing. It was a one liner. Its purpose was determine if a door was outside of normal working hours. This was something that could be given a meaningful name as a function, and it is much better than seeing the ugly expression. So size alone cannot dictate if something is too small. In general, I think you will instinctively know if something is too small. If the calling function doesn't become simpler due to the presence of this other smaller function, then the small function is not helping. Usually, if I spot this situation, it is an indication that the problem has not been factored very well. It is like d...