Skip to main content
4 of 11
added 71 characters in body
user avatar
user avatar

For your particular case I'm going to provide an unusual answer and suggest erring on the side of bigger and meatier, but not too big, functions unless you're working in a functional paradigm where things get quite different.

It's because you already seem to have the instincts to refactor and stamp out duplication. You're not the type to write a 300-line function and fail to think twice about it. In those cases, with your instincts in the right place, your tendency will be towards modeling thoughts at too granular of a level to the point where they lose a lot of the richness of meaning and clarity.

Simplicity Can Add Complexity

When you have too many granular functions and classes and so forth in your system modeling the teeniest of human thoughts, it can be even more difficult to comprehend than one with moderately-sized classes and functions. It's because there can often be more intellectual overhead to understanding the interaction between functions and objects than just understanding what they do on their own. One big function, in that sense, can be more difficult to comprehend on its own, but potentially easier to understand as a whole. Meanwhile tiny little functions all calling each other but fulfilling the same purpose as the former bigger (but not too big) function with a complex graph of interdependencies can be much harder to understand when you zoom out and try to figure out everything that's happening for a meaningful operation, since the reader can't just sit down with one function and figure out the big picture. They instead have to end up wading through many functions back and forth trying to keep track of all the state changes along the way, and that's much harder than comprehending a moderately-sized singular function that models a clear human thought.

As a simple example, a 2D video game interacting with granular, object-oriented Pixel objects isn't necessarily easier to comprehend that one interacting with coarser Image or Sprite objects, even though the latter case will have bigger functions that do more things inside than the Pixel version. Pixel is too granular of an idea to model what the bulk of what a video game is interested in doing. Even as an implementation detail of Images or Sprites, having the logic separated and dispersed between a teeny Pixel object and bigger Image object could actually make the code more difficult to comprehend at the level of the image operation being performed.

You can potentially make more gains in maintainability and clarity and the ability to reason about what's going on in your code at a meaningful level by seeking to reduce the number of interactions that go on between functions and objects. That can potentially get you further than trying to dice up the objects and functions into smaller and smaller pieces if the exchange requires introducing a spiderweb of interactions in your system.

Granularity Losing Meaning

An extreme example is to start writing "helper" functions, named as such, simply to reduce a couple of lines of code repeated 3 times (6 LOC) to 3 lines of code calling a "helper" function which can't really be documented very well beyond the fact that it slightly reduces the code required to implement functions A and B. In that case the "helper" functions are about as intuitively designed as what a machine might generate for a "code reducer" software which would only reduce the maintainability of the code. In that case, I'd even suggest just writing a bit more code even if it's "boilerplatey" if you can't reduce the amount in a clear and meaningful way.

Similar to CandiedOrange's answer, your functions should model a clear idea with an obvious purpose, easily documented. If you're working at too granular of a level and too entrenched in the code that you're writing functions so granular that you can't do this effectively anymore, that's when I'd suggest to err on the side of bigger functions.

Balancing Act

Of course some clear ideas with obvious purposes just map directly to a line of code. A function to retrieve the number of bytes remaining from a file stream probably doesn't need more than a line or two of code, but represents very clear functionality and one which will frequently be desired by those who use the code. Those are fine. Some functions need to be very short, but not all need to be. It's not always the case that shorter is better. It's a balancing act.

So when does the function get too big? That's tougher to answer and will be something you hone with experience. But instead of focusing on the number of lines of code, some basic things to look at:

  1. Side effects. Does the function cause changes to states outside of its scope, like modifying the parameters being passed to it or mutating the data members in a class? If so, a function that causes many side logical effects is often very difficult to comprehend as well as error-prone if it ever needs changes. So try to reduce the logical side effects to one max or ideally zero.

  2. How many local variables does the function need to manage? This isn't as bad as side effects, but a function that needs, say, a dozen local variables visible to many lines of code in the function is also difficult to comprehend.

  3. Does the function perform a clear, singular responsibility? This question is related to human thought processes. A function to DestroyWindow might need to do many things computationally but still models a very clear human thought.

  4. How many levels of indentation do you need (assuming indents are used for blocks)? Linus Torvalds once said something along the lines that if your function requires more than 3 levels of indentation, it's broken and needs to be fixed. While I don't believe in putting hard numbers like this, a function that needs a lot of nested blocks and scopes often requires you to push and pop to/from a complex mental stack in your brain as you trace through it. If you find yourself reaching for many levels of indentation, that might be a time to consider refactoring.

user204677