top | item 36680325

(no title)

larksimian | 2 years ago

I agreed with their assessment though for a totally different reason: the second function is only doing primitive operations and not invoking any other functions.

For the same reason I found that their refactoring actually made the first function worse. Now I have even more jumps that I need to make in order to 'inline' all the code so that I can deeply understand what the function is doing.

There seems to be an implicit assumption that you should just trust the names of the functions and not look at their implementation. This is hopelessly naive. In the real world, functions don't always have obvious names, their implementation can involve subtleties that 'leak' into usage etc.

I strongly dislike function extraction that's driven by anything except the need to reuse the code block. Function extraction for readability is about as useful as leaving a comment above the code block. Honestly I'm more likely to update the comment than the function name since changing the fn name means updating the callers as well.

edit. To add a bit of nuance: I think 'primitive' vocabulary is what's essential here. The standard lib of a language is primitives. The standard functions of a framework like Ruby on Rails are primitive.

I'm happy to see code written by calling functions/types/operations that I have seen before dozens or hundreds of times. What I don't like is when a feature in a codebase has created it's own deep stack of function calls composed out of functions that I know nothing about.

Create a rich base vocabulary that is as widely shared as possible and use that for your work. Avoid creating your own words as much as possible. This way I can glance at your code and not just see if (BLACK_BOX_1 or WHAT_DO_I_DO_AM_I_LYING) then DO_SOMETHING_BUT_MAYBE_I_ALSO_DO_SOMETHING_ELSE_WHO_KNOWS

discuss

order

AnimalMuppet|2 years ago

If I have a function call to WHAT_DO_I_DO_AM_I_LYING, that's no better than a block of code with a comment that says "WHAT_DO_I_DO_AM_I_LYING". The difference is that, once I look carefully at the code and find out what the called function does and that the name (optimistically) isn't lying, then the called function only takes up one line in the code I'm looking at, whereas the in-line block takes up several lines (plus the line for the comment). For me at least, the function call takes less mental space (if the function name is accurate).

larksimian|2 years ago

It all depends on how often I'm interacting with an area of code. Is it library code I'm using all the time or some obscure private internal that I visit once a year at most?

If I'm only reading that code like ... 1 time ever, I'm never going to add to memory all the dozens of random 2-line private functions someone broke up their 100 line procedural function into. I'm going to be annoyed and mentally 'inline' it all into one linear block of code and then forget all about it.

Therefore I'll never remember or trust the private function names and will never know what the module does at a glance. I would be better off with it just inlined into one big procedure in the first place. Don't pollute my mental function cache.

The more some code gets touched and read and updated the more I can justify adding an internal vocabulary to it. Again, something like Ruby on Rails has a huge API surface area compared to plain Ruby, but that's fine, because after using it for a decade I know most of it and it was worth learning the vocabulary since I'm constantly using it.

It's like... commonly reused library or std lib or framework code is useful jargon used and reused by a community of people. But that pattern doesn't downscale. Applying it and creating my own jargon for an obscure module in a code base is a bit like someone inventing slang all on their own and whining when people ask them to explain what they mean in plain english. I don't want to read Finnegan's Wake for my job, just give me some boring realist prose suitable for a 5th grade reading level.

bbwbsb|2 years ago

I think the ideal here, though I haven't seen it in actual production code - it may not be feasible/ergonomic yet - is to use a sufficiently capable type system and property-based testing so that it is possible to read from those what functions do even if the names and documentation are replaced with random strings.

In such a case the probability a function does something other than what it says on the tin is almost 0. If I am using Forth, the probability of WHAT_DO_I_DO_AM_I_LYING doing something naughty is much higher. In Haskell, it's already pretty unlikely.

Maybe preferences in this regard depend on language and peer trust?