6 ms·
And functions should be no bigger than your head.
by 83457 6y ago
And functions should be no bigger than your head.
- warent 6y agoWell this explains why the most difficult people to work with always write the most disgustingly large god functions
- 83457 6y agoBecause they have big heads?
- modeless 6y agoThis is my least favorite programming advice. Splitting functions for no reason other than "it's too long" is a bad practice. https://news.ycombinator.com/item?id=25263488 https://news.ycombinator.com/item?id=25263488
- tenaciousDaniel 6y agoMost of the illegible code I've ever written was because I kept splitting functions up, thinking I was following good practice, but was really just making emotional/aesthetic decisions. When you go back to edit your code, it's like calling a 1-800 number and getting re-routed to 15 different departments to finally find the person you need to talk to.
- abernard1 6y agoI really agree with this. I see this all the time with "business rules" problems. If you have a situation where you can't make levelled abstractions, you've got some thorny interconnections in your logic (and those may be fundamental!!!). The way I handle this is with a "gauntlet pattern." You still can split your logic up into parts, but you just do it by "rejecting" certain logical chunks at a time within a function and comment above each state of the gauntlet. It looks something like this: // marketing told me we should never do this ever under any circumstances if (!a) { return CASE_1; } // if the user enrolled like this, we should check if there's this bad property about that user if (!b) { return CASE_2; } // oh, you crazy people in finance if (c > 4 && d < 0) { return CASE_3; } return CASE_4; The key thing is not to get hung up on duplication or having exact control flow like how the business thinks about it. You want 1) to return the largest percentage or simplest cases out first, 2) keep everything flat as possible without layers of branching, and 3) be able to associate tests to each one of those early-reject lines. The nice thing about this is the reduction of cognitive load. By the time you get to the 3rd case or whatever, your mind can already know certain things are true. It makes reasoning about rules much easier in my experience.
- m12k 6y agoI think the trick is to split it the eight way. If you do it the wrong way, you end up with functions becoming layer upon layer of indirection that feels like a rabbit hole you need to dive further and further into to understand what is actually going on. But if instead you keep the core control flow in the original function, but move sensibly named chunks of into into helper functions - that way the original function ends up reading like pseudo-code, and you don't really need to actually look at any of the helper functions to understand what is going on.
- modeless 6y agoThe "right" way to split, as advocated in the article I linked, is not based on naming but based on state. Have all your split out functions be pure. Retain all the state mutation in one place where you can keep an eye on it.
- cma 6y agoUse an editor with folding and this is just unnecessary indirection and jumping around to read a linear series of steps that happen one after another. If a function is only called in one place, it should rarely be a function. It may help a small amount when viewing a stack trace, so you can tell at a glance without looking up the line and jumping to the section, if that is a big enough advantage, can make it a lambda and leave it all in place as long as the debugger/stack trace will list the lambda assignment name.
- luhn 6y agoThis is the first time I've heard "functions should be no bigger than your head" and I actually really like it. Rather than prescribing some arbitrary function length, it highlights that functions are meant to be understood and the appropriate length should come from that.
- lmm 6y agoThat advice should be understood in the same way as "sentences should be no longer than a few dozen words" or "paragraphs should be no longer than a few lines". Of course adding a line break at an arbitrary point in a long paragraph doesn't make it better. But a solid wall of text is a red flag.
- the_cat_kittles 6y agoive developed a defacto rule of thumb / intuition that a function size should basically be as big as it can be without being inconvenient to test. which means that its pretty small most of the time
- jim-jim-jim 6y agoI thought I hated small functions too, til I realized I just hated scrolling. The moment it's off my screen, it's out of my head. Assuming you just need helper functions for f(), compare int f(int x) { return g(h(x)); } int g(int x) { return ... } int h(int x) { return ... } to f = g . h where g = ... h = ... Turns out my language choice was the problem, not over-abstraction. If you can fit it all on one screen, then have at it tbh.
- modeless 6y agoThe logical conclusion of this line of thinking is APL or K. Some people swear by it. I admit terseness is appealing for solo coding, and I loathe the bloat of Java etc, but when taken to the extreme terseness starts being a problem for collaboration.
- jim-jim-jim 6y agoI think you're right about those langs and collaboration; all the procedures are right there in front of you, but you gotta keep track of the data shape in your head. Just as much work as scrolling page-spanning functions imo. That's what makes point-free Haskell such a sweet spot for me: terse, symbollic control flow and nice combinators, but types to guide you. I try to use it as a better J.
- dathinab 6y agoI can't really agree to this. Through without questions there are always things you should not split out. Splitting a functions which consists of multiple logical steps is most times a good idea because it makes testing much simple and tends to show you where you accidentally had subtle cross-cutting concerns or unintended cross-talk between sub-domains (domains in sense of modelling, not http). What is important is to properly name functions (and if you can't you probably shouldn't write that function). Also it's important to learn how to live with abstraction, i.e. to reason about code wrt. a specific problem without needing to jump into the implementations about every function /method it calls. Through the later point is much easier with a reasonable use-full type system. And with this I mean useful for abstraction without making abstraction to hard and without allowing to many unexpected things. Languages like rust or scala have such a type system (as long as you don't abuse it) but e.g. C++ fails this even through it has a powerful type system. At least this are is opinion. And without question if you can't cleanly split something out, then don't split it out. If you can split it out but not name it reasonable either your understanding it lacking or it should never have been split out.
- chewbacha 6y agoWhat if my cranium is extra large? Do I get to write bigger functions?
- brobinson 6y agoPython's developers must have enormous heads if that's the case: https://github.com/python/cpython/blob/b8fde8b5418b75d2935d0ff93b20d45d5350f206/Python/ceval.c#L984-L4125 https://github.com/python/cpython/blob/b8fde8b5418b75d2935d0...
- cinntaile 6y agoIt's mostly a giant switch case for opcodes though, that's not really what is meant here.
- rightbyte 6y agoThey could have had inlined functions or macros if they would have wanted to keep the function lenght down for the table.
- rightbyte 6y agoGod I love reading code like that. Makes me feel better about the complex mess I produce thinking if it could be done in a clean way.
- AnimalMuppet 6y agoDon't write functions bigger than your co-workers' heads, either.