Ask HN: How to deal with the short vs. long function argument

5 points by da39a3ee ↗ HN
I've been a programmer for 25 years. A realization that has crept up on me in the last 5 is that not everyone thinks that functions should be short: there are two cultures, with substantial numbers of excellent programmers belonging to both. My question is: how do we maintain harmonious, happy, and productive teams when people can disagree strongly about this issue?

The short-functions camp holds that functions should be short, tend toward the declarative, and use abstraction/implementation-hiding to increase readability (i.e. separable subsections of the function body should often be broken out into well-named helper functions). As an example, look at Peter Norvig's beautiful https://github.com/norvig/pytudes. For a long time I thought that this was how all "good programmers" thought code should be written. Personally, I spent over a decade writing in a dynamic and untyped language, and the only way that I and my colleagues could make that stuff reliable was to write code adhering to the tenets of the short-function camp.

The long-functions camp is, admittedly, alien to me, but I'll try to play devil's advocate and describe it as I think its advocates would. It holds that lots of helper functions are artificial, and actually make it _harder_ to read and understand the code. They say that they like "having lots of context", i.e. seeing all the implementation in one long procedural flow, even though the local variables fall into non-interacting subsets that don't need to be in the same scope. They hold that helper functions destroy the linear flow of the logic, and that they should typically not be created unless there are multiple call sites.

The short-function camp also claims an advantage regarding testability.

Obviously languages play a major role in this debate: e.g. as mentioned above, untyped dynamic languages encourage short functions, and languages where static compilation makes strong guarantees regarding semantics at least make the long-function position more defensible. Expression-oriented and FP-influenced languages encourage short functions. But it's not obvious, e.g. Rust could go both ways based on the criteria just mentioned.

Anyway, more qualified people could and have written at much greater length about the topic. The questions I'm asking are

- Is it "just a matter of taste", or is this actually a more serious matter where there is often an objective reason for discouraging the practices of one or other camp?

- How can members of the different camps get along harmoniously in the same team and the same codebase?

11 comments

[ 3.3 ms ] story [ 38.4 ms ] thread
An important quality of a professional software engineer is being able to work in a team and being able to deal with personal differences. And those difference go beyond coding styles. With respect to coding styles, I always tell junior software engineers to not change the code formatting, the names of classes and methods, and abstain from refactoring code just fit it to your preferences.

My personal preferences is to keep the code base as small as is needed for the moment. I avoid creating functions/methods that are only called once. If possible, (like in C#) I prefer using local methods and lambda expressions. I try to avoid code dupplication (beyond the trivial). I am carefull with introducing abstractions as often it makes it harder to navigate and/or debug the code.

Thanks. Your attitude sounds reasonable. Being honest though, one problem is that I do not consider it a personal preference: I basically think that functions longer than 100 lines are completely, and objectively, unacceptable (I wouldn't normally write a function longer than, say 30 lines). They are just woefully failing to take advantage of modularity, and modularity is the entire basis of the software revolution in the last 60 years. I recognize that I should try not to have overly strong feelings about a subject that some consider a matter of taste, but I don't think I'm alone here.
And what is your attitude when you in some project come across a method longer than 100 lines? Do you immediately start cutting it into a bunch of smaller methods passing long list of input/output parameters? Or even turn it into a class to contain all the local variables?

Or do you immediately perform a git blame and confront the developer who wrote the code to lecture them or force them to rewrite the code?

Or do you think: I would not have written it like that and it might result in some technical debt, but at the moment it just works, and I just forget about it for the time being?

(I have deleted my comment because reading adjacent comments made me realize it's not a productive conversation)
There are answers to that scattered throughout this thread. Essentially, long functions are not taking advantage of modularity. And modularity is how we make complex things understandable, testable, and reliable.
It depends whether the function is conceptually important to the project and will be read a lot by colleagues, e.g. new team members who are learning the codebase.

If not, then a bit like your last option (forget about it).

But if it is important, then... that's the problem. I consider it a real problem. But it punctures colleagues' egos to have it pointed out that their code, while correct and effective, is actually not very well written. And colleagues in programming tend to have large egos that put up a forceful defense when they are being punctured.

I think the difference between a long function and many short functions that are not reusable is not big. A long function can be cluttered if it is really too long, and also many small helper functions can bring confusion to the code. The advantage of short functions can be their testing, or the division of work in the team. Anyway, if too long functions or too many one-call functions start to appear in the code, the code architecture should be reconsidered.
A major difference between a long function and multiple short helper functions is that when you look at the local variables in scope in the long function you do not easily know which of them are used by which parts of the function. You see a variable used on lines 756-762 but the function continues to line 900 and perhaps it's used again further down?

Some languages have block scopes that can make this better; my position is against long functions in languages that lack block scopes or written by programmers that do not avail themselves of block scope.

I really struggle to see why anyone is not attracted to that form of abstraction: if local variable foo is computed in 10 lines using local variables bar, baz, and qux, and those 3 are not used for anything else, then clearly matters are improved in a 100-line function by replacing those 10 lines with

  foo = computeFoo(bar, bax, qux)
Authors of long functions are not embracing appropriate modularity, but that seems inexplicable, since modularity is the basis of the software revolution.
Personally, I encounter long functions (as you described, around 100 lines) mainly in older codebases that were written over 20 years ago. In many cases, I get them for rework when developing new software, particularly in industries where the adage is 'if it works, don't change it.' This phenomenon could be attributed to the fact that, at that time, OOP was just emerging, and developers were not yet accustomed to it. I strive to write code that is concise and highly readable. It's important that the function's purpose and usage are immediately evident at first glance, rather than relying on copious comments.