back

by jay_kyburz·13y ago·view on hn ↗
I don't think naming your functions has to make the code "significantly longer". Perhaps 2 lines per callback.

And yes it can be hard to name functions, and I think the OP has not created very good example, but a good name that accurately describes what the function does means I don't have to read it if I don't want to.

A better example would have been some node.js code that has 5 levels of nesting, each with a some significant code within.

You've just got to name them.

2 comments
The examples are 40% and 20% longer. That is significant.

The trouble with small examples is that their problems don't seem like a big deal because their entire codebase isn't a big deal. You have to apply them many times over to get any sense of their compounding effect, which is what really matters. But by definition, that's out of scope of a discussion whose examples are small. The only way out of this paradox is to pay close attention to apparently small effects.

One of the things that really annoys me about modern JavaScript programmers is that they seem to have forgotten the basics.

Functions are good. Short methods are good. Reusability is good. Properly naming your functions is good. Separation of concerns is good.

Complaining about a couple of extra carriage returns and a few extra characters to properly name the method is insanity.

How many functions are good? If I add a dozen, is that better? Short methods are good—have any evidence for that? I don't think you do, because such evidence as there is points in the opposite direction (look it up in Code Complete). It's proper to name functions properly? I agree! What's "properly"?

Hammering these dogmas harder doesn't make them any truer, it just creates a feeling that you know what you're doing in a "If the English language was good enough for Jesus Christ then it's good enough for the state of Texas" kind of way. We have almost no empirically verified knowledge about software development. One of the few things we do know (sort of—the studies are old and shaky) is that code size is a predictor of error rates. Think that's relevant?

Short methods/functions are good in that they do very little and thus are easier to read and grasp than a function spanning 50-100 or more lines. Short methods/functions are also easier to name properly.

As Clojure, F#, Haskell and friends show, it's better to have 100 functions working on one datastructure, then having ten functions for ten datastructures.

gruseom's comment raised the bar for evidence in this discussion, and gave some examples of evidence. "As Clojure, F#, Haskell and friends show" - can you point to a study?

Relevant link: http://www.slideshare.net/gvwilson/bits-of-evidence-2338367

No study no. But since those languages are built around that line of thinking, and becouse those languages are great, and becouse that line following your quote is in alot of the books about learning said languages, I belive the statement to be correct.
That line is by Alan Perlis [1]. It's great, but not relevant to the question above, which is: all other things being equal, what's the best way to do functional decomposition? If you're moving between 1 and 10 data structures, then "all other things" are so far from being equal as to make for a completely different matter.

[1] http://www.cs.yale.edu/quotes.html

The important difference in those FP languages is a) they encourage pure programs with little side effects. This is much more friendly towards small functions. b) You have the freedom to create functions only where you need to. In the OP's example, he is forced to create a new named callback after each IO call.
The reason those languages are more friendly towards small functions has nothing to do with pure functions/programs. You can use the same concepts in C/C++ or Java/C# just as easily, I've done it, it's not hard.

What makes those languages more friendly towards smaller functions has to do with how easy it is to create a new function in those languages. Creating a function in C/C++ for instance, is a pain becouse of the boilerplate involved (header files...).

In the OP's example, he's imposing named functions on himself when he doesn't actually need to. For some reason he also declares functions within the same scope that he calls it, which just makes it cluttered in my oppinion. He would probably be alot more happy with promises.

> How many functions are good?

As many as needed.

> If I add a dozen, is that better?

If your programs path needs to do 12 things, then yes. Each function should do one thing. If in documenting your function, you use the word and, that's a safe bet that you are actually writing two functions as one.

> Short methods are good—have any evidence for that? I don't think you do, because such evidence as there is points in the opposite direction (look it up in Code Complete)

Unless there is another reference I missed, Code Completes reference referred to no more than a screen's height, which at that time was ~ 25 lines. Assuming not every line is a line of code performing some action, I'd still consider that a small function.

> It's proper to name functions properly? I agree! What's "properly"?

That's hard to answer, which is why so much has been written on it. Couple that with community idiosyncrasies (like Obj-C naming vs Java), you have a lot of "proper" ways to define functions. Within the realm of the community standards, I subscribe to the thought that the method names should tend to be verb oriented. Talk about actions, or perform an action. After all, functions do something.

> We have almost no empirically verified knowledge about software development.

Well, there is quite a lot. Much of it's older (in the history of software).

> One of the few things we do know (sort of—the studies are old and shaky) is that code size is a predictor of error rates. Think that's relevant?

Be careful about this. It's not as open and shut as you'd like to think. Indeed, it's about as verifiable and confirmed as cyclomatic complexity is. Without going into too much detail and sticking strictly the context here: "code size" is an awful metric.

By that notion:

    int a = b + c; 
Is dramatically better than:

    int hours = timesPerformed + hoursPerPeformance;
Layer that into the concept of lines of code: is that lines of functional code? Lines of any code that isn't a comment? Any lines that aren't a comment? Lines of code that are only functional? All lines, including comments? All lines, including comments, except for standard headers?

All of those are valid deductions. After all, while it might seem obvious to remove comments, they could cause confusion, right?

And this ignores even the basic question: is a line merely a line on the screen? Or is it a languages statement? A single operation? Would a complex for clause with multiple assignments and statements count as one line?

All this is to say that code size is not the holy grail of error rates.

My own though is that taking CC and LoC and putting them together is best. A function with low complexity and with fewer lines of code is better than a longer function. Many smaller, lower complexity functions are also better than a single highly-complex function that is longer than any single function, but smaller than all the others when combined.

Lets see, in the 2nd edition of code complete, section 7.4 "How Long Can a Routine Be?" has a bunch of studies listed. Lets take a look:

1. A study by Basili and Perricone found that routine size was inversely correlated with errors; as the size of routines increased (up to 200 LOC), the number of errors per LOC decreased (1984).

2. Another study found that routine size was not correlated with errors, even though structural complexity and amount of data were correlated with errors (Shen et al. 1985)

3. A 1986 study found that small routines (32 LOC or fewer) were not correlated with lower cost or fault rate (Card, Church, and Agresti 1986; Card and Glass 1990). The evidence suggested that larger routines (65 LOC or more) were cheaper to develop per LOC.

4. An empirical study of 450 routines found that small routines (those with fewer than 143 source statements, including comments) had 23% more errors per LOC than larger routines (Selby and Basili 1991).

5. A study of upper-level computer-science students found that students' comprehension of a program that was super-modularized into routines about 10 lines long was no better than their comprehension of a program that had no routines at all (Conte, Dunsmore, and Shen 1986). When the program was broken into routines of moderate length (about 25 lines), however, students scored 65% better on a test of comprehension.

6. A recent [sic!] study found that code needed to be changed least when routines averaged 100 to 150 LOC (Lind and Vairavan 1989).

7. In a study of the code for IBM's OS/360 operating system and other systems, the most error-prone routines were those that were larger than 500 LOC. Beyond 500 lines, the error rate tended to be proportional to the size of the routine (Jones 1986a).

8. An empirical study of a 148 KLOC program found that routines with fewer than 143 source statements were 2.4 times less expensive to fix than larger routines (Selby and Basili 1991).

The 25 lines is just one of the studies, related to students of CS in particular. In general, it seems like the data indicates that somewhere between 100 and 200 is optimal for minimum errors per line, lowest cost to modify, less need to modify, and overall undesirability. So the "short, 5-10 line functions" seems to be not only entirely unsupported by empirical data, but actually harmful in general.

Thanks for that. Though, LOC seems to fall into the "What is a line of code trap."

Study 2 also supports complexity with errors, not size (small or large).

I hadn't realized how old these studies were, however. For some reason, I thought they were from the 90's. I'd be wary about relying on, what is, frankly outdated assumptions.

I'd be wary about relying on, what is, frankly outdated assumptions.

You'd be wary, and so you'd replace these studies with what exactly? Which of their assumptions are outdated?

Let's not fall into the trap of thinking that programmers before us didn't understand as much. As far as I can tell, the issues were much the same. A greater problem with older studies is that their sample sizes were mostly small and their experiments never replicated.

Without going into too much detail

Please do go into detail. What is some of this "quite a lot" of knowledge that sheds light on the questions at hand? It would be great to have new references.

"code size" is an awful metric

This has come up on HN before and the evidence does not support what you're saying. The gold standard in recent work [1] found that more complicated metrics have no value beyond simple program length: "After controlling for size none of the metrics we studied were associated with fault-proneness anymore."

it's about as verifiable and confirmed as cyclomatic complexity is

Do you mean they're both awful? Actually, if what you say is literally true, it follows by Occam that cyclomatic measurements are worthless, since they are far more complicated and no more valuable than the most trivial metric there is.

The studies may be weak and often old, but at least there is a body of evidence around this, and the body of evidence points reasonably consistently to the conclusion that code size is (a) the best measurement of complexity that we have, and (b) the best predictor of error rates. (The only other one I know of that has repeatedly been verified is code inspections, and that's a process not a metric.) Contradictory evidence is welcome.

It's true that there's a debate around how best to measure code size. But that's a separate question. That also has received some pretty high-quality discussion on HN in the past--see the link below. It changed my thinking, for one; I highly recommend the comments by anon_d in that thread.

[1] http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.20.2...,

via http://blog.vivekhaldar.com/post/10669678292/size-is-the-bes...,

discussed at https://news.ycombinator.com/item?id=3037293.

Number of tokens is a better metric than number of characters.
Yeah I'm sick of that obvious strawman being trotted out by the function-bloater side everytime we try to talk about how code size is important. I'm sure there's other examples where small code size isn't necessarily better -- but nobody bothers to look for them!

In practice I find I'm a terrible judge at where the function boundaries should be. Several times I've tried to reorganize a project where functions had gotten gradually too big, ended up making them too small and numerous and having a pain debugging, then finally settling on some intermediate position. All the overly-certain talk of "as many functions as you need" misses these nuances.

(I wrote recently about global vs local readability: http://akkartik.name/blog/readable-bad)

> Yeah I'm sick of that obvious strawman being trotted out

So, you think it that code should be measure by the size of the code?

a = b + c; is better than the other?

Care to explain why?

I never suggested in any way that number of characters was a good metric. Indeed, I suggested quite the opposite.
I think tlrobinson's point is that number-of-tokens renders a trivial objection to measuring code size such as "what if we rewrite everything to have one-letter variables" irrelevant, since that doesn't change the number of tokens.
That's a book that's citing sources from over 20 years ago, before unit testing, improved tools, compiler warnings and code analysis tools were even available. Massive changes have happened to the way we code since then. Hell OO had only just started entering the conciousness back then. We're not even talking about things like DI, just simple concepts like OO.

Code Complete still has many lessons to teach, but it's certainly not gospel in this day and age (unfortunately my copy is buried in some boxes from a move and I can't find it at the mo to re-read that chapter!).

I personally have seen massive changes to the way we code, people just seem to understand how to split out code into more logical chunks than they used to. Even as recently as 8 years ago, when I start professionally, I remember people used to constantly ask how to pass variables by reference, global variables, all sorts of nastiness that meant that methods weren't really being used properly. These days I honestly haven't seen a by ref pass or a global variable for years (apart from in an old, truly terrible VB.Net program I've just been given to maintain & update, ugh).

Programming really has changed massively. And there certainly are fairly famous proponents of short functions out there, Kent Beck, Robert Martin. It's advice that's changed over the last 20 years, but I don't know whether there's studies to back it up, all I can give you is that the general consensus seems to have changed.

Back to the point, I certainly don't hate long methods, what I hate is methods that do many different things. That's just bad code in my book, especially if they start bleeding lines into each other and get jumbled up as tends to happen as your code ages.

I admit it's very easy to go too far down the rabbit hole, with a massive tree of confusingly nested methods and it's a balancing game.

But when you see a long function you can usually tell straight away whether it's justified or not. Is it doing a complicated thing that justifies a hundred or two hundred lines or is it just doing loads of little simple things.

And often if it's doing simple things, invariably other functions in the same code file will also be doing those simple things with tiny variations and it can be refactored to reduce the TLOC and improve maintainability when you need to update that functionality. And that's usually a sign of a programmer how hasn't learnt to do DRY yet and is relatively inexperienced (in the .Net world anyway, I'm not having a dig at all, I appreciate js still massively changes every year).

A function that does one thing and one thing only is:

- generally short

- testable

- name it intelligently and then others don't even have to read the code to know what it does

- easier to spot bugs

- functionality doesn't get mixed up

- reusable code becomes a lot more obvious

Some of these hold especially true with javascript given its lack of block scope.

Long functions are generally a strong code smell, in my experience anyway. They generally contain bad code, regardless of whether they'd have been broken up or not. Functions that start declaring random anonymous objects, which I see a lot in javascript, are especially dubious. It's an artefact of javascript's design that because it's so difficult to declare reusable objects and namespaces and manage code between files a lot of javascript programmers just end up writing these huge blobs of code.

As to where I see this happening in javascript, as an example I was reading Meteor.js's code the other day and was actually surprised at just how bad the code is. Every function seems to do 5 different things, it declares object right in the middle of functions rather than extracting it out, loads of frankly crazy coding practices.

EDIT: In fact, now that I think about it, I remember once using that bit of CC to argue that long functions were better discussing it with a friend. Funny how you can do a complete 180 over time.

>A function that does one thing and one thing only is: >- generally short >- testable > ...

This is true only if the function decomposition is intelligently done. The problem is that with callbacks, the function decomposition is forced on you based on what needs to be done asynchronously (i.e., mostly I/O). As a result, it breaks apart the flow of control into little tiny pieces that may be only loosely coupled with the most logical way to decompose functionality.

So your statement may be true in general, but it may not be applicable when it comes to how to best handle callback functions.

The problem with callbacks is they are not about reusability — they force you to organise your code around I/O and not around your app logic... hence the names like wtfFriendAction.
There is a difference between callbacks and lambdas. Nothing forces you to provide a lambda as a callback. If that lambda will only ever be used once, then it's ok as long as it's short and easy to understand what it does.

If that function is required multiple places, you make a proper function out of it, and pass that function instead of a lambda.

Callbacks allow for abstractions like map, filter, reduce... Used properly, they can actually increase reusability instead of prohibiting it.

It's like you didn't read my comment — how functions like wtfFriendAction help reusability?
In this small example it doesn't, getFriendsById however, does. I would imagine that wtfFriendAction could be used multiple places though.

wtfFriendAction is additionally a very, very poor name. It should be called "addFriendToFriendList" or something. Another thing is that the author himself states that the function doesn't actually makes sence, hence the ugly name.

> Another thing is that the author himself states that the function doesn't actually makes sence, hence the ugly name.

That's the point — using callbacks you end up with functions which do not make sense...

Edit: spelling

IMO, the biggest problem is the extra indirection, not the raw lines of code.

As for names, the problem is that he is forced to make a new callback whenever he does an IO operation and then needs to make up a name for it on the spot. Ideally you should never be forced to split code into functions unless you actually want to do so and when you can choose where to create functions its much easier to create good abstractions and good names.