News for hackers. Stuff that matters.
Re:The Economics of Clean Code by dcolkitt on Tuesday February 11, 2020 @12:00PM (#22300020)
> Keeping code "clean" is more about how it represents the concepts and relationships it is dealing with and less about whether it follows any specific style rules.

I really disagree. To take a prime example, short functions are almost always better than long functions. Regardless of the content, codebases that are refactored into smaller, more self-contained functions almost always improve in reliability, performance, readability, and extensibility.

I think the reason for this largely comes down to reducing the surface area of interactions. A 100 line function can introduce subtle interactions between the first and 99th line. Whereas a three line function, the graph of potential dependencies is much smaller.

A similar logic extends to smaller, more self-contained code blocks, classes, modules and even applications.

Re:The Economics of Clean Code by heisenbit on Tuesday February 11, 2020 @12:17PM (#22300207)
> A 100 line function can introduce subtle interactions between the first and 99th line. Whereas a three line function, the graph of potential dependencies is much smaller.

Interesting. And what happened with the other 97 lines of code? A fair comparison would be a 100 line function and maybe 20 functions of 5 lines and maybe 20 additional lines for function signatures plus maybe 20 lines coordinating the 20 functions. We also then have introduced 20 function names.

Re:The Economics of Clean Code by mumblemumble on Tuesday February 11, 2020 @01:00PM (#22300719)
> To take a prime example, short functions are almost always better than long functions.

This one is so very disputed, though.

My own sense is that it's not the length of the function, it's the length of the loops and the complexity of the branching. Short functions help when they prevent those from occurring. But, in cases where neither is present, I haven't seen a refactoring to short functions that I thought was an unambiguous improvement. Even that variable from line 1 influencing line 99 doesn't end up being particularly difficult to see or understand as long as the control flow is linear.

Re:The Economics of Clean Code by dcolkitt on Tuesday February 11, 2020 @02:43PM (#22301945)
Even without branching or loops, it's still a problem. Every line in a code block shares a namespace and state-space with each one another. If there's one very consistent lesson in the history of software engineering, it's that state is a necessary evil to be minimized. Stateful code almost always is less reliable and harder to reason about than pure code. Small functions keep the surface area of stateful interactions strictly delineated by the arguments and return parameters.

The more lines of code that exist in a block, even with linear control flow, the exponentially higher chance that one line inadvertently clobbers something that subsequent line depends on. Consider a function like

    def foo (msg: str):
      head_index = find_header(msg)
      ... # 50 lines of code
      log(msg, head_index)
      ... # 50 lines of code
      return msg[head_index] 
Now say somebody adds some chunk of logic in the middle

   def foo (msg: str):
      head_index = find_header(msg)
      ...
      head_index += offset_digest(...) # Needed for log formatting
      log(msg, head_index)
      ...
      return msg[head_index] 
A seemingly innocuous change to the function, has inadvertently clobbered the value of a variable we're relying on in a slightly different context. With 50 lines of code separating each reference to the variable, it's very easy for this bug to fly under the radar unnoticed.

An engineering culture that emphasized small functions avoids this problem, because the change would instead move the logic to its own self-contained function. That avoids the problem of new code accidentally clobbering the old code.

    def foo (msg: str):
      head_index = find_header(msg)
      ...
      log_offset(msg, head_index)
      ... # 50 lines of code
      return msg[head_index] 

    def log_offset (msg, head_index):
      head_index += offset_digest(...)
      log(msg, head_index)
Re:The Economics of Clean Code by Silhouette on Tuesday February 11, 2020 @03:15PM (#22302317)
The problem you describe is also fixed by any language that is explicit about stateful behaviour and effects. It's not an inherent property of having a long function, just of having insufficient control over mutable state (or poor discipline in using the available controls).
Re:The Economics of Clean Code by dcolkitt on Tuesday February 11, 2020 @03:43PM (#22302629)
Even if you have strong mutability controls built into the type system, reasoning about correctly applying those constraints is far easier using small code blocks.

If a variable is declared at the beginning of a block, its use has to be audited throughout the entire block. It's a lot easier to confirm that a variable is never mutated across 4 lines than it is across 200.

When the size of a code block exceeds the capacity of working memory (about 8 chunks), then devs will skip the cognitive effort and fall back to the most permissive types by default. Not always, but bugs mostly come from the codebase at its worse, not its best.

Considering your point from the other direction, the most mutability disciplined codebases tend to naturally bias towards short functions. In my experience most long functions are long, because the locally-scoped variables are being used to haphazardly track statefulness across the control flow. To contrast, Haskell's immutability and purity strongly restricts that pattern. Unsurprisingly, one of the most striking features of Haskell codebases are how short the functions tend to be compared to something like Java.

Re:The Economics of Clean Code by Silhouette on Tuesday February 11, 2020 @04:06PM (#22302848)
If a variable is declared at the beginning of a block, its use has to be audited throughout the entire block.

Well, if the variable is declared at the beginning of the block and is scoped to the entire block, we have two possibilities.

One is that the variable really is relevant throughout the block, or close to it. In that case, full mutability makes it more difficult to understand the behaviour, but if the variable is immutable or can only be mutated using explicitly controlled tools then the audit you mention is trivial.

The other is that the variable is only relevant to some relatively small part of the block. In that case, it is possible that the function is trying to do more than one thing and that part of it could beneficially be separated into its own function, taking any variables that are only relevant to that part with it. It is also possible that the variable is being declared prematurely and starting its lifetime earlier than necessary, in which case it may be better to move the declaration later so its lifetime is shorter and the area where it is in scope is reduced.

The latter cases do tend to lead to shorter functions, or at least reduced scopes, and for good reasons.

However, what you can't do is magically take a mutable variable that is genuinely relevant throughout some algorithm that really is complicated enough to require 200 lines of code to describe it, say the current state of some fundamentally complicated state machine, and make that complexity go away just by factoring out smaller functions. You're still going to have to pass the variable or some equivalent around, and you're still going to need to mutate the state in lots of places. But now your logic for doing so is scattered across 50 4-line functions, and to establish the true behaviour you don't just have to audit one of them, you have to audit them all and all the code that connects them.

Re:The Economics of Clean Code by sli on Tuesday February 11, 2020 @04:16PM (#22302946)
> It's a lot easier to confirm that a variable is never mutated across 4 lines than it is across 200.

It's even easier when the language you're using doesn't let you mutate or rebind in the first place. You mention Haskell codebases having short functions, but that isn't an automatic given. Haskell and other ML languages are quite friendly to long functions specifically because the language guarantees no mutation, among many other reasons.

In fact, the Elm project explicitly tells Elm developers to not worry about the length of functions because of this property. You can't accidentally mutate or change something and not notice, because you simply aren't allowed to do that.

Re:The Economics of Clean Code by Silhouette on Tuesday February 11, 2020 @03:12PM (#22302284)
To take a prime example, short functions are almost always better than long functions.

This is a popular claim in some circles, but I don't think it stands up to scrutiny.

As others have observed, while you may gain locally through having simpler individual functions, you may also lose globally because you now need to manage the complexity of how all those functions relate.

There seems to be very little evidence to support the argument that shorter functions are objectively better, and if anything the evidence we do have suggests that extremely short functions are objectively worse in terms of things like defect density.

I would rather argue that functions should do one thing well. This will tend to correlate with shorter functions rather than 100+ line behemoths, but the important thing is that a function represent a single, coherent idea. Maybe that idea can be expressed as some neat one-liner in a functional programming language. Maybe it's some 200+ line ordered sequence of conditions that systematically determines how to shift between states in a state machine that is modelling a fundamentally complicated situation.

Re:The Economics of Clean Code by Floegipoky on Tuesday February 11, 2020 @03:22PM (#22302387)
Hot take: the "short functions are better" camp is really just accidentally arguing for pure functions and immutable data. If that 100 line function has 3 side effects and ends up broken into 10 smaller functions, you probably now have at least 7 pure functions.
Re:The Economics of Clean Code by iampims on Tuesday February 11, 2020 @12:15PM (#22300191)
But some other function will need to stitch all the small functions together, and you’re kind of back to square one, no?
Re:The Economics of Clean Code by jasonwocky on Tuesday February 11, 2020 @12:27PM (#22300343)
Not really, since the dependencies between them are now explicit. Yes the stitching is its own complexity, but divide-and-conquer is a pretty time-honored approach to effective problem solving.
Re:The Economics of Clean Code by jcelerier on Tuesday February 11, 2020 @02:03PM (#22301484)
> but divide-and-conquer is a pretty time-honored approach to effective problem solving.

only if you can also divide and conquer the amount of persons that will dev, because now when there was one name to learn and assign meaning to, there are ten different names.

I am speaking as someone who used to have a hard limit on function length (20 lines, in Allman style :-) ) - this ended up being really harmful and creating way too complex code sometimes.

Re:The Economics of Clean Code by aluth on Wednesday February 12, 2020 @08:26AM (#22308589)
Of course it was harmful, you were focusing on the wrong thing. The point of having methods with no more than [insert number] lines isn't because having a method with more than [insert number] lines is inherently bad. It's because long methods are usually an indication that the method is breaking the single responsibility principle. I get the impression that a bunch of programmers read Robert Martin's work, put no thought into what he was saying about short methods, concluded that the line count was the problem, and began proliferating that idea.

To give you an example, when using Java's stream functionality, I tend to insert every method call in the chain after stream() in a new line, for readability:

  ...
  entityIds.stream()
      .map()
      .filter()
      .collect();
  ...
When I scan the method to see if it should be broken down, the fact that this code occupies 4 lines doesn't really contribute much to my decision to refactor the method into several methods, because those 4 lines are accomplishing one thing.
Re:The Economics of Clean Code by Floegipoky on Tuesday February 11, 2020 @02:34PM (#22301825)
Function application is THE killer feature