3 ms·
I'm not sure the first part of the article is based on a sound methodology. For example, we don't have the number of classes for each bucket in the plots [1] an
by leto_ii 6y ago
I'm not sure the first part of the article is based on a sound methodology. For example, we don't have the number of classes for each bucket in the plots [1] and [2]; perhaps there are only a few classes of one liners - probably not a statistically representative sample.
It's also not clear what type of code is counted. I'm not convinced boilerplate (setters, getters, simple constructors) should be taken into account. It will artificially decrease the average method length in a class. Imagine a class with 10 fields (hence 10 setters, 10 getters, maybe a few constructors) and one method of 100 lines. The average method length will be 120 (100 + 20*1 from the boilerplate) / 21 ~= 6 lines of code. If there are bugs in this class, they basically have to be in the large method; still, in statistics this would count as a class with pretty short methods overall.
I'm also surprised by what counts as a long method - in my experience code starts getting messy with lengths > 20 - 30. These methods don't even seem to be represented in the data.
> application code should probably favor larger functions and avoid ad-hoc abstractions
Why favor large functions? Maybe allow them, but why encourage? Also, we haven't established what large is (20 lines - perfectly fine; 200 lines - I don't think this can ever be justified).
> If there is a small piece of functionality that seems re-usable, it should usually not be moved to a separate function, but to a separate library.
This does seem excessive. Don't you think there might be code that can be locally reused? Does every little thing have to go to a library?
[1] https://i2.wp.com/softwarebyscience.com/wp-content/uploads/2020/08/Screen-Shot-2020-08-24-at-9.13.53-PM.png?resize=768%2C564&ssl=1 https://i2.wp.com/softwarebyscience.com/wp-content/uploads/2...
[2] https://i1.wp.com/softwarebyscience.com/wp-content/uploads/2020/08/Screen-Shot-2020-08-24-at-9.15.45-PM.png?resize=768%2C563&ssl=1 https://i1.wp.com/softwarebyscience.com/wp-content/uploads/2...
- tsimionescu 6y agoYour points about getters and setters are valid, and may be skewing the results for Java especially. However, I believe they should be expected to be skewing them in the direction of improving the numbers for very small methods, not harming them. > I'm also surprised by what counts as a long method - in my experience code starts getting messy with lengths > 20 - 30. These methods don't even seem to be represented in the data. This is a good point, and I believe it is the main reason the article talks about Very short functions being sub-optimal. I think almost everyone wod agree that a function in the range 1-5 lines of code is very short, while deciding if a 20-Loc method is short or long is going to be more disputed. > Why favor large functions? Maybe allow them, but why encourage? Also, we haven't established what large is (20 lines - perfectly fine; 200 lines - I don't think this can ever be justified). My idea is to favor expressing serial logic serially. That is, if you have to do A then B then C then D, prefering to write it that way, rather than A, then foo() [which does B and C] then bar() [which does D]. This implicitly favors large functions over short ones, assuming that the code is required at all, of course. Short code is still preferable to long code, but at the module level, not function level. > This does seem excessive. Don't you think there might be code that can be locally reused? Does every little thing have to go to a library? There might be, of course, this is not about strict commandments. But I think that looking at many code bases, there are numerous functions called only once, which essentially act only as comments. A lot of the time, it may be worth it to refractor the code more deeply, and instead of simply extracting a piece of a larger function into a non-reused smaller function, to try to extract some common pattern into a library, and to keep the business logic in-line in the original function. Just as an example, if you have something like for(int i=0; i <n1; i++) { for(int j=0; j <n2; j++) { arr3[count++] = arr1[i] + arr2[j] ; } } As part of a large function, instead of extracting this into an 'addArr1AndArr2' function, to extract the general pattern and leave so thing like this in the original context: arr3 = zip(arr1, arr2).map(a1,a2=>a1+a2); Of course, this will not always be possible, and sometimes a function can just become too long even in serial logic. But a lot of the time, the effort of reducing line count by extracting or applying library functions will be more worth it than simply reducing function line count by more simplistic refactoring.