11 ms·
Small functions considered harmful
- coroxout 9y agoI find a lot to agree with here. It's all very conceptually neat and (if you're lucky) easy to read from the top down, where you enter one function and read off a list of other functions which are called in order. But then if you look into any of those other functions they also call more functions and so on, several levels deep. And when you have to debug someone else's code because the data after function 15 of 17 isn't quite right, and you have to unpick all the places it's been passed through in slightly different versions and slightly different lists of parameters, it can be a nightmare. Same with my linter telling me to close a file within a few lines of opening it. Personally I'd rather keep all the file-munging code in one place rather than scatter it down a rabbithole of nested functions as an exciting Alice In Wonderland story for future developers. I try to come to a compromise on these things when working in a team, though...
- deleted 9y ago[deleted]
- wiz21c 9y agoThis is a very valuable comment. Indeed pushing the abstraction to small functions actually remove abstraction in the "system of function". So there is a tension. what I find super interesting in your comment is that you solve this as a team. So, my point is, this demonstrates (a bit) the fact that coding is also a social activity. Of course, anything a team produces is a reflection of the team itself. But here you pinpoint the fact that you adapt your solution to the team as well as to the problem itself (anyone else would have said "I choose this solution because it's the best for the problem") I like that :-)
- jacalata 9y agoWouldn't the compromise be unit testing?
- geezerjay 9y ago> Wouldn't the compromise be unit testing? I agree. Unit testing is done to ensure that all components work exactly as they are expected to work. If any component fails to work but developers only notice it "because the data after function 15 of 17 isn't quite right" then it appears that something is very wrong with the way those units are being tested.
- kazagistar 9y agoI tend to agree, but not with your particular example; the boundry between how you manage a resource and what you do with it seems like the perfect place to put an abstraction. The file mingling can be in one big function, but there are benefits to separating it from the open/close logic.
- moxious 9y agoShortness in a function is correlated with quality in design, but it doesn't cause quality in the design. When we simply follow formulaic advice (keep all of your functions short) we lose sight of the wisdom behind why this was wanted in the first place. The goal is to develop the wisdom, that makes you a great engineer, not to "follow all of the rules"
- chinhodado 9y ago> The goal is to develop the wisdom, that makes you a great engineer, not to "follow all of the rules" This is why books like Clean Code can be harmful. It can be extremely dogmatic if blindly followed, which is very common unfortunately.
- Denzel 9y agoClean Code can be harmful? Or the person dogmatically applying the advice? Martin warns against such blind dogmatism. He even goes so far as to explain the thought process behind most of his recommendations so that one may consider whether the rule applies or not. On balance, Clean Code does far more good than it does bad. I have yet to see these dogmatic Clean Coders blindly applying Martin's advice. Not in industry and certainly not in open-source. It's painful to work with most OSS. Eugene Schwartz, one of The Godfather's of advertising, when asked, "How long should an ad be?" liked to say... as long as you can hold the reader's interest. Much like Martin would say when asked how long a function should be... as long as you're working at the same level of abstraction.
- shados 9y ago> I have yet to see these dogmatic Clean Coders blindly applying Martin's advice. You're lucky. I've seen it plenty of time. In one extreme case we even had to let go a perfectly (formerly) competent dev after he read that book and went crazy (yeah, seriously).
- slindz 9y agoThat is a blog article I would read. Doubly so if it was on Coding Horror.
- Sergesosio 9y agoFunctions exist to prevent code duplication not for commenting code, that's what comments are for.
- theonething 9y agoI believe most rubyists would disagree with you. I had a ruby loving coworker that made you feel like a moral failure if you commented your code instead of making it 'self-commenting'
- d--b 9y ago> The idea that functions should be small is something that is almost considered too sacrosanct to call into question Errr... Really?! I thought we all agreed that the first rule of programming style is that "it depends"... When they say "small functions", they mean "not the 5000 loc VBA macro that has 50 Boolean arguments, and 30 side effects". Breaking a function that does 1 thing into sub functions just so that each of them is smaller is not a good thing. And I think people realize that fairly quickly.
- jghn 9y agoDefine "a thing". This is the crux of all of these debates, IMO. Most people agree a function should do one thing, people tend to disagree on the granularity of things.
- exergy 9y agoWhich is precisely why debates like these can occur in the first place, it's not an exact science. Which is also why the parent started with "It depends".
- calafrax 9y agoyes it is actually. if your function has a logical branch then it is doing more than one thing. if your function has a logical branch within a logical branch then it is doing exponentially more things and this complexity continues to grow exponentially. therefore, if you have a logical branch you ask yourself: does it make sense to create a function here? the answer may be yes or no. if you create a nested logical branch you ask yourself: does it make sense to create a function here? the answer still may be yes or no but the weight of the evidence for yes has increased exponentially.
- Veedrac 9y ago> When they say "small functions", they mean "not the 5000 loc VBA macro that has 50 Boolean arguments, and 30 side effects". Clearly, though, not all of them do. From the article, for context. > The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that. > In my Ruby code, half of my methods are just one or two lines long. > Any function more than half-a-dozen lines of code starts to smell to me, and it’s not unusual for me to have functions that are a single line of code > I’ve worked on codebases inherited from folks who’d internalized this idea to such an unholy extent that the end result was pretty hellish and entirely antithetical to all the good intentions the road to it was paved with. It seems to me there are a lot of people who espouse making your functions extremely short, and for who a 30 line function counts as long.
- matttproud 9y agoBetter Stated: (overly) DRY considered harmful. This is one thing about Go programming culture and its prevalent idioms I appreciate: low context switching cost.
- deckiedan 9y agoI wrote something myself trying to figure out my thoughts about this a few years ago (note: I was sleep deprived when writing it...): https://madprof.net/nerdy/refactoring-really/ https://madprof.net/nerdy/refactoring-really/ There certainly has been a over-emphasis in some quarters on 'each function should only do one thing' and even 'most of my functions are only one or two lines long'. Possibly because it makes it easier to write tests for, and be certain you've covered all of the possible options. You just then end up writing 4 million tests. It's all about clarity, I think, but different people find different things clearer in different situations.
- jstimpfle 9y agoIn a program, there's a lot to optimize for - minimize depth of call stack. It's easy to lose track in a deep call stack. - minimize function overhead - names that have to be invented - function calls and arguments that have to be written (and read again). - many small functions: "ravioli code" where it's really hard to distinguish functions by their "function" - minimize/localize state - if it's easy to separate significant state into a function, do it. (Not making a statement about objects. They are long-lived and their state doesn't go away after the first call). - DRY - Multiple identical or similar code blocks are an opportunity to make a function. We can roughly categorize into essentially (conceptually) and accidentally similar code, and the latter case is not an indication for a new function. - obvious thread of control - simple code has the nice property that it's easy to understand by following or mapping out sequentially. By contrast, highly abstracted or callback-heavy code is hard to understand at a global level. - consistency - ... helps understanding, but can also cause an implementation to be 5 or 10 times longer if applied dogmatically. It's good to read all these articles ("avoid state", "avoid long functions", "avoid objects", "avoid functional programming", "avoid abstraction") and to deeply understand them. Which probably means making all the mistakes on one's own. In the end it's important to know in which situations a particular style works well, and to not be dogmatic when choosing a style. I would say a good program tends to have both large and small functions, and the large functions tend to be at the top of the abstraction stack.
- rhinoceraptor 9y agoI think you can avoid a lot of these problems by using small inner functions.
- dotdi 9y agoAside from the clickbait-y title, I find it quite disturbing to base that whole piece on, what even the author describes as, problems in "codebases inherited from folks who’d internalized this idea to such an unholy extent". Indeed, small functions can be bad if you completely and utterly overdo it. But wait, that's true of nearly everything else.
- d4nc00per 9y agoYeah, the idea of small functions, and the overuse of DRY are separate.
- copyconstruct 9y agoAuthor of the article here. Nope, the article doesn't conflate DRY and small functions. But the quest for DRY can lead to an explosion of small functions, which isn't necessarily a good thing. The vice versa is true as well - many programmers, in their quest to make functions as small as possible, end up DRYing it up to the fullest extent as well. There's a relationship between the two, but DRY and small functions aren't synonymous themselves, and neither does the article suggest that anywhere.
- throwanem 9y agoObsessive decomposition of the sort Fowler, for example, is cited preferring, very quickly becomes pathological - I suspect that the codebase Fowler describes in that tweet, to the developer as yet unfamiliar with it, reads like one of those old IBM field engineer manuals where a giant circuit diagram is spread across 800 letter-size pages, all bordered with dozens of numbered arrows each referencing the page on which a given trace continues. But I sort of feel like Sridharan throws the baby out with the bathwater, too. I mean, in the CRUD example, carefully chosen abstractions make the code easier, not harder, to read - if I'm working to comprehend how the application handles UI state changes, I don't want to have that effort complicated by a bunch of user-creation-related database interaction; I'd much rather that be in a method call so I can deal with it it when I care about user creation, and ignore it when I don't. Same for email messaging and event log injection. And I have to say that my experience gives me to think the idea of preferring duplication over abstraction is just completely, wildly off base. I mean, sure - any given abstraction is likely to change over time as feature requests and bug reports come in. That's just the job. But if the stuff that needs to change is abstracted, it only has to change in one place. If it's not abstracted, then it has to change in N places across the entire application, not all of which are guaranteed to be easy to find - after all, you probably don't have distinctive method or function names. Hope you've got good tests! Except you don't. Or maybe you do - I never have, at any point in my career where I've worked with a codebase in which copy-pasted code was prevalent, because such a codebase is a sign of an engineering culture that's far too weak to support investment in automated testing.
- al2o3cr 9y agoBut if the stuff that needs to change is abstracted, it only has to change in one place. IMO, the point of "prefer duplication over the wrong abstraction" is that "all the stuff that needs to change" is a partially-to-totally unknown quantity. You can either guess at the time you have the minimum possible amount of knowledge (when the code is initially written) or wait and make that decision later with more evidence. Even good tests won't save you from premature abstractions with the seams in the wrong places when a change request that cuts across them comes in; they will help as you try to disentangle multiple use cases that go through the same code paths that now need to be different, but it's still a mess. Or worse - you might not disentangle them, and wind up with "generic" code littered with 'if special_case' fragments.
- deleted 9y ago[deleted]
- calafrax 9y agoThis discussion is predicated on the concept that function size is calculated by the number of lines which is completely wrong. function size (function complexity actually) is measured primarily by indent levels not length and when there are multiple indent levels with nested branches and loops this is when you are supposed to create functions. length is not really an issue in most cases.
- mannykannot 9y agoMost of the arguments in the article are agnostic to the size metric - offhand, I cannot think of any claim that is invalidated by changing to your measure. Furthermore, it is the proponents of short functions quoted in the article who are using line count as a metric, so it is not as if the author of this article has created a straw man by choosing a misleading metric.
- jacalata 9y agoMost of the arguments in the article seem agnostic to the size of the function altogether, which is hilariously ironic given the complaint about the "long functions are bad" example at the start of the book.
- mannykannot 9y agoMost of the arguments concern the side-effects of splitting a given unit of work into a lot of functions, which is an inevitable consequence of the strategy being deprecated.
- twii 9y agoSo, the author thinks this is harmfull?: var area= ( width, height ) => width * height; If not, it is just clickbait for me.
- Clubber 9y agoIf I'm understanding you correctly: It would be harmful if you were calculating something subject to change, like, say price. price = (cost) => cost * 1.10; And you sprinkled that throughout your code. The problem arises when the business says they want to be able to change the logic so that it lowers the price for customers that have subscribed over a year. Now you have to change the logic everywhere that shows up, rather than having a single function where you change it once: function decimal Price(cost) { .. } That in essence, is why DRY is important: code maintenance & refactoring. Even your example it could be dangerous: var area= ( width, height ) => width * height; What if the business was making sandboxes and they wanted to be able to add a short wall around it so cats have a harder time using it as a bathroom. It would then be: var area= ( width, height ) => (width + 1.0) * (height + 1.0); Again, if you had sprinkled that throughout your code, you would have to search and find every instance of that and change it. This is also harder to test, because if you miss one, chances are, it won't show up immediately.
- sickbeard 9y agoInterestingly the author provides so many reasons why you should use small functions, and less convincing reasons why you should not. Generally, smaller functions are better than the problems that come with their implementations. Also is it normal to add so many tweets to an article?
- blunte 9y agoThe goal is not to make small functions for the sake of making small functions, but it's to compartmentalize some functionality into a nice, easier to reason about thing (function). Then you compose these easy to reason about things into more complex, but yet still easy to reason about things. For people like me who struggle to maintain multiple layers of complex abstractions in our minds, being able to see a small function and say, "Ok, I trust this one - it does X." makes it easier to navigate up and down through the abstractions. Perhaps part of my appreciation comes from living in Clojure and Elixir (after many years of several OOP languages).
- yen223 9y agoSmall functions are certainly more pleasant to work with in languages that support first-class function composition. Context switching is less of a problem when you can build large functions with only a couple lines of code, just by stitching together multiple small functions. One big reason why Unix's "one thing well" philosophy works so well for Unix is the ability to chain commands together cheaply, with pipes and redirects.
- copyconstruct 9y agoAuthor of the article here. >The goal is not to make small functions for the sake of making small functions, but it's to compartmentalize some functionality into a nice, easier to reason about thing (function). This tendency is exactly what the article highlights -- this need to compartmentalize, when taken to extremes, makes code a lot harder to read. Not everyone does take it to extremes, but many programmers are partial to what I call "the smallest viable function" syndrome, and ergo don't stop compartmentalizing until they've abstracted away every last piece of logic. The article states that: "Thus, a “single level of abstraction” isn’t just a single level. What I’ve seen happen is that programmers who’ve completely bought in to the idea that a function should do “one thing” tend to find it hard to resist the urge to apply the same principle recursively to every function or method they write." >For people like me who struggle to maintain multiple layers of complex abstractions in our minds, being able to see a small function and say, "Ok, I trust this one - it does X." makes it easier to navigate up and down through the abstractions. And that's the problem - needing to maintain what you very aptly call "multiple layers of complex abstractions". This especially hurts programmers new to the codebase (or worse, the language), since they have to juggle so many different layers of complexity. The article calls for reducing this complexity, instead of stacking more and more layers of abstractions in the name of "clean code".
- whack 9y agoDuring my earlier years, I would get into all types of dogmatic debates, such as "DRY-considered-harmful" or "small-functions-considered-harmful". With experience, I've realized that such abstract debates are generally pointless. Any principal can lead to bad results when taken to an extreme, or badly implemented. Thus leading to people declaring that-principal-considered-harmful, swinging the pendulum to the opposite extreme, and beginning the cycle all over again. Now, I find such discussions valuable, but only in the context of concrete examples. Devoid of concrete and realistic examples, the discussion often devolves into attacking strawmen and airy philosophizing. If this article had presented realistic examples of small functions that should have been duplicated and inlined, I think we can then have a much better discussion around it. That said, I do have to offer a word of warning. It's possible that the author is a good programmer who knows how to incorporate long functions in a way that is still clear and readable. Unfortunately, I've had the misfortune of working with horrendous programmers who write functions that are hundreds of lines long, duplicated all over the place, and are a pain to understand and maintain. Having short-functions and DRYness is indeed prone to abuse, but it still works as a general guideline. Great programmers may be able to ignore these guidelines, but at least it prevents the mediocre ones from shooting themselves (and others) in the foot.
- narag 9y agoUnfortunately, I've had the misfortune of working with horrendous programmers who write functions that are hundreds of lines long, duplicated all over the place, and are a pain to understand and maintain. As a maintanance programmer I had that misfortune very often. But I don't believe it's really so relevant to the article. Having short-functions and DRYness is indeed prone to abuse, but it still works as a general guideline. It doesn't, it's more of a heuristic and, as such, a way of detecting that you might be doing it wrong, not a sound rule to apply. The problem is exactly that one: people that use "write short functions" as something that you must do. I've suffered such a fool as a boss and it was really sad. The man believed that two lines function (and the calling spagetti that followed) were a superior system to ten lines functions that accomplished a definite goal. In general the problem is people that use general rules without understanding them.
- 9y ago
- enqk 9y agoMy theory is that the small functions motif appeared with practitioners of languages where the function is the only tool introducing a new scope / block for variable definitions. Languages like Python or Javascript. What about Smalltalk? In language where blocks can be introduced at will (stricter Algol descendants like Pascal/C/C++/Java) or are introduced by control structures, then the need for such rule is much less necessary, and only the harm done (friction, readability) by fragmenting and obscuring logic remains.
- alkonaut 9y agoone problem with the example of functions A() B() C() Called in sequence is that they smell of imperative code. A recipe of some kind, of steps performed in sequence. That kind of code, when it occurs, should probably just be performed in a single function. That is - until either A, B or C can be re-used by other code without creating an unnatural abstraction. If the steps as separate functions can be tested separately - great. But if they are only ever used in this sequence - do you really need to test them individually? What good does the breaking up into functions do, compared to just some comment text in a longer method. // Step A: ... 3 lines // Step B: ... 4 lines // Step C: ... 3 lines Answer: very little. And it forces the reader to scroll to see the relevant code. Some languages allow the use of local functions - which is basically just some trickery to help with variable scope and not have to use a comment line to call the sub-step something. Can be quite useful. A better example of 3 functions is when you have y = A(B(x)) and then turn it into y = A(B(C(x))) If the C can have some kind of semantic meaning (e.g. C just fetches the price of item x before the rebate is applied by B). In this kind of functional code there is usually very little harm in making more and smaller functions. Not sure where I'm getting with this but I assume It's kind of an argument for avoiding procedural code to begin with, and aiming to make actual functions.
- sqeaky 9y agoThere are several advantages you are disregarding. Functions have names and you get to name the code in a way that shouldn't expire the way comments can. In most languages separate functions create separate scopes. This prevent incidental use of temporaries from one section into another. Having a smaller scope to look at means that refactoring or changes to new business requirements can have a smaller place for side effects to occur. I agree that testing each part on its isn't required, but if it becomes part of the public API and A is setup database connection and C is teardown Database connection and B is use the database connection then as one function B is really hard to test. Breaking it into three functions lets you write unit tests for at least B by mocking A and C.
- alkonaut 9y ago> Functions have names and you get to name the code in a way that shouldn't expire the way comments can. Sure that's a benefit, but often times I think the scrolling is a downside that is underrated. The code being right in front of you has huge value. > In most languages separate functions create separate scopes. This prevent incidental use of temporaries from one section into anothe Right. Thought I mentioned that. That's why I think local functions are good. Gives the best of both worlds. It's a proper name instead of a comment, and gives the right scope. > but if it becomes part of the public API Yes obviously API design is a separate (and MUCH) harder question than refactoring for breaking logic apart I think.
- moocowtruck 9y agoI feel like an article such as this should be full of code examples, just talking about this or that in which there are many dependent situations in which "it depends" just makes me get sleepy
- sqeaky 9y agoEven the highest voted post here is commenting on the value of examples and lack of value in higher order discussion. I must discard the post because everyone I have known professionally with similar opinions was grossly incompetent. Some strong examples would make it foolish for me to disregard this out of hand.
- _pmf_ 9y agoI blame "Clean Code"; it's recommended reading, but at its core it dumbs down refactoring to mechanically factoring out common code without actually crafting abstractions. I'm still in the process of unlearning this. The prime directive should be "craft sensible abstractions", not "avoid duplication at any cost"; even more so when we're talking about actually modular software, where duplication is much easier to tolerate than blurring responsibility lines. (I'm generally not a big fan of Uncle Bob.)
- blickentwapft 9y ago"Considered harmful"?!? Why, this must be both IMPORTANT and LEGITIMATE, and the author has AUTHORITY. When a negative opinion carries such an academic, aloof, and professional sounding headline then I am inclined to give it great credence. This guy didn't just say "here's something I think is bad", he said it in fancy and upmarket words. Let's closely follow what they say..... Seriously, HN should have code that auto flags anything including a subject line of "considered harmful".
- Newtopian 9y agoA function should be X Lines long... but no longer where X is any number of lines necessary for implementing the ONE thing that function should be doing Any attempt to replace X with a concrete number will invariably sacrifice simplicity for the sake of that number.
- andybak 9y agoThis was interesting: > an awful lot of times, pragmatism and reason is sacrificed at the altar of a dogmatic subscription to DRY, especially by programmers of the Rails persuasion. I'm not making this a tribal thing but it does sometimes feel like there's a cultural tendency in the Ruby and Javascript communities to... well... preach a little bit. All advice is flawed and most maxims are only partially true. If a community bounces from one "one true way" to another all the time then it's probably not a particularly healthy environment for those who are learning - as they tend to lack the experience to put advice into the correct context.
- realharo 9y agoThis old article explains it perfectly https://whathecode.wordpress.com/2010/12/07/function-hell/ https://whathecode.wordpress.com/2010/12/07/function-hell/ Over-optimizing for local readability can hurt global readability in the end.
- laythea 9y agoFunction borders should be drawn by defining abstractions, not by counting lines of code. Sometimes, the most useful abstractions involve large functions.
- agentultra 9y agoIt seems like the author's idea of the term abstraction is limited to substituting procedures with functions (or worse, class methods). The claims that "all abstractions leak" and that adherence to DRY makes code "hard to follow" is what gives me this impression. This line of thinking happens if you think of code in procedural terms. And if your sense of abstraction is to hide procedural side-effects behind function applications then yes... I can see where you might get the idea. A real abstraction like lambda doesn't leak. Using Monads to compose side effects doesn't leak. These are mathematical abstractions and we use them all of the time: even in programming and even if you don't identify as someone who's any good at maths. Learning the lambda calculus, algebra, and first-order logic will take you much farther than thinking in procedural steps. Composing more interesting functions from smaller ones removes so many errors that procedural code has: removal of superfluous bindings, a more declarative style, and it makes code more more easy to reason about during refactoring: using familiar tools from mathematics we can manipulate expressions and types. This is where abstractions really shine: you can manipulate higher-level expressions without caring about the details of those at the lower level. This only really happens if you care about purity in languages that don't do it for you and can reason using such tools.
- jerf 9y agoArguably lambda, monad, etc. are generalizations, not abstractions: http://www.emu.edu.tr/aelci/Courses/D-318/D-318-Files/plbook/absgen.htm http://www.emu.edu.tr/aelci/Courses/D-318/D-318-Files/plbook... Abstractions that don't leak aren't abstractions, as the essense of abstraction is the simplification and removal of detail. Monad is a generalization of a pattern seen in many places, so it doesn't have to "leak".
- taeric 9y agoI call shenanigans. Lambdas can easily lead to leaking call backs as your implementation. Similarly, monadic style can leak if you are sloppy with it. Just see coffee in Haskel where the IO Monday has infected the entire codebase, and not just a boundary.
- 9y ago
- euroclydon 9y agoIf you want to have an easy time refactoring code later, forgo OO patterns that have properties and methods in the same class. Instead, make classes for your data, and make sure to give each class a deep clone method. Then, your logic goes in static functions that do not alter the input, but rather spit out new or cloned versions of the data in the output. Then you can reason and refactor at the method layer and not worry about hidden side effects.
- sqeaky 9y agoThat seems a bit extreme compared to where many teams are at, but I can see how it could work. How would you compare that to reducing the size of classes to bound where side effects can happen?
- nabla9 9y agoFunctions have several functions. * Functions can be like new words in a language. If there is new concept that is used frequently, it should be named or abbreviated and maybe listed in a index. * Functions can sometimes be like chapters or sections. They are entry points listed in table of contents (API description) * Functions are sometimes used like paragraphs. Used only once to make very long section easier to read. Not really functions. Way to structure text. For paragraph use we might want blocks of code with function like interface. Code editors could collapse and expand them. let a, b, c = 100 let p = 0 paragraph "Intro to foo" (constant a, modify p) { let k, l, m, ... } assert (p < a)
- imron 9y agoFor me, I generally try to break in to smaller parts any function where the logic extends over one screen in length. There are of course always exceptions to this, but having logic take up roughly a single screen size makes it easy to reason about.
- sqeaky 9y agoI have a 4k screen at some point this pattern stops working.
- jondubois 9y agoThe loss of locality argument is a very good one. Having to jump around different files whilst holding layers upon layers of abstractions at the back of your mind to figure out the source of a bug is overwhelming and extremely distracting (you literally need to keep a call stack inside your brain to pull that off). DRY is all fine until you need more information about the function than what the function name and documentation can provide - Sometimes you actually need to peek inside the code itself. I don't think that code can ever be 100% black box; especially as you move up higher in the chain of abstraction. This is particularly true for dynamically typed languages - These days in JavaScript I often find myself peeking inside the function's code before I invoke it - It's very easy to make false assumptions about the behaviour of the function based on its name alone and often the documentation isn't enough and doesn't tell you anything about the performance of the function (is it O(1), O(n) or O(n^2)? - You wouldn't want to call an O(n) function whilst in a loop over n because then you'd get very crappy O(n^2) performance).
- DanHulton 9y agoAnything, taken too far, considered harmful.
- mnarayan01 9y ago1. Functions which are "longer than they should be" decrease code quality. 2. Measuring the "number of lines per function" is extremely easy. Code quality tools have a bad tendency to equate (1) and (2) for the same reason that the proverbial drunk searches for his keys under the streetlight. This has legitimately bad consequences. This is further compounded by the way that small functions ease mock-based testing. While certainly attractive in the abstract, when a code base is overly influenced by this I find that it is substantially more difficult to understand via inspection. All that said...I find the whole "X considered harmful" formulation almost unbelievably annoying. Here it doesn't even make any sense.
- alexandercrohde 9y agoI think the author doesn't understand the small-function-philosophy (at least not the same way I do). Let me clarify how I see it: - Build a ton of small functions that are reusable across any project. You are essentially making useful concepts. (i.e. a library). Bottom-up. - Once you have those, a business problem often will only be about 3 or 4 of those powerful, reusable functions. So something like sending out a newsletter might end up being: function sendNewsletter(letter) { database.getAllUserRowsAsIterator().forEach((row) => { sendmail(x.address, letter); } } Now if we want the whole newsletter not to fail if there's a single exception, we can make another reusable construct "count exceptions" that's a wrapper function that catches all exceptions and builds a hashmap. If you want this to work in a larger project, this requires having reliably unit-tested code and doc-blocks so that other people can reuse your abstractions, and then having roughly comparable coding skill to you.
- maxxxxx 9y agoHow about "following a rule blindly without understanding its rationale and limitations considered harmful?". People need to understand what they are doing and why and make judgement calls.
- hbt 9y ago>>> If you have to spend effort into looking at a fragment of code to figure out what it’s doing, then you should extract it into a function and name the function after that “what”. Fowler is right about smaller functions and OP misinterpreted his statement. This is what Fowler means https://gist.github.com/hbt/3e71146454a2d6388338af1d76394a13 https://gist.github.com/hbt/3e71146454a2d6388338af1d76394a13 Abstract the fragment of code in a function, keep it within the function until you need it elsewhere and do not pollute needlessly the API with functions that are poorly named and used one time only.
- voidhorse 9y agoProgramming is like writing. There are some general rules that will usually make your meaning clearer. There are also exceptions. Begin following the rules, prioritize clarity and semantics, identify when breaking a rule would make your meaning clearer and break away. At the end of the day, you're still just communicating--in communication clarity is key. You must also keep your audience in mind. Thus, if your company follows conventions you stick to them, as your audience will be able to digest said conventions quickly. It's all language. Programming only has the additional wrinkle that you are also communicating with a computer--which prioritizes very different things than human readers (efficiency, memory management, etc.)
- novembermike 9y agoIIRC the original concept of DRY wasn't "Don't Repeat Yourself" (that's the pithy version that's easy to remember but wrong). I think it was more like "A piece of knowledge should be found once in a code base", which isn't quite as easy to follow or remember but cuts to the issue more deeply. The problem isn't superficial duplication. I might do a .map(x->x*2) in multiple places, but if they represent different pieces of knowledge (money in one, age in another) then I may not be Code DRY but I'm still Knowledge DRY. An easy way to tell the difference is to think about changing one of your functions. If you'd be happy that it changes the behavior everywhere in the function then you're Knowledge DRY. If you're scared, you're just Code DRY.
- zmmmmm 9y agoNicely put. The problem with DRY is it is expressed in term of "repeating" so it suggests that code repetition is the problem. The flip side of achieving DRY is breaking isolation between two pieces of code. That's a downside - so DRY is a tradeoff, not a simple one sided win. The real questions are things like "Is it likely if I change logic A, that B will also need to change in the same way?" and "Is it likely that if there's a bug in B, that would also be a bug in A?". If those things aren't true, you are shooting yourself in the foot because you will certainly be going in and changing A and B to maintain them and now every time you do that for one you are at risk of breaking the other. DRY is usually right, but it definitely comes at a cost.
- sqeaky 9y agoThere were no real examples of when long functions helped. I can come up with 30 or examples of long functions being bad in the codebase I am working in now. I stopped reading about halfway through as the contrived DRY twitter post. I did search the article for "test" and a few other keywords and found more flowery words without substance. All the systems I have seen with long functions have had more bugs and greater difficulty to test. All the systems lacking DRYness have invariably had bugs because things weren't updated in all the places they needed to be. I have seen a clear correlation. I know that correlation is not causation, but the mechanism is simple and easy to understand: Long functions have more places for bugs to hide. The mechanism for dRY to help is equally simple:DRY code means fewer things need to be changed to fully cause a correct change. Both of these reduce the place for bugs to exist and my experience matches that. I understand that my experience is that of only 1 person, but I have held 9 different software development contracts in the last decade and seen the full range of long to short functions and I have seen DRY and non-DRY code. This pattern has followed into open source code I have inspected. If I am wrong it will take strong concrete examples of DRY or short functions hurting, and I mean links to repos like github.
- copyconstruct 9y agoAuthor of the article here. That's primarily the reason why there are no "concrete examples", because one person's concrete example is another person's definition of contrived. Splitting hairs over some toy example wasn't something I thought would buttress the ideas presented, though I can imagine why some might need that scaffolding to follow along. Re resting - here are some points the article makes about how smaller functions can in some cases hurt testing: 1) "Furthermore, when the dependencies aren’t explicit, testing becomes a lot more complicated into the bargain, what with the overhead of setting up and tearing down state before every individual test targeting the itsy-bitsy little functions can be run." Disambiguating this for you, one of the myths of smaller functions is that they are easier to test. The article makes the claim that this isn't always true, because many who wax lyrical about the beauty of smaller functions also champion for fewer arguments to be passed to the function. The book Clean Code very explicitly states this (read the book, not as a how to guide but as a cautionary tale), and many a time what I've seen happen in the wild (especially in Ruby) is that programmers who like small functions and don't like making deps explicit - which then means the code (and ergo tests) rely on setting up shared global state. Of course one can write smaller funcs with fewer args and not do this, but that's not the point here. The main argument is that making functions smaller doesn't always make it easier to test. The article also does provide two examples when having the smallest possible function does help in testing. I'm not going to rehash those arguments again here.
- no89ntoui 9y agoI don't necessarily disagree with the post, but I will say that in almost 30 years of programming, I can only recall one programmer who took small functions to a harmful extreme. On the other hand, I can recall dozens who took long functions to a harmful extreme. So yeah, it can be bad, but I see the other extreme way more. (That may be because I don't do enterprise programming, though.)
- joevandyk 9y agoI've never been served wrong by constantly asking these questions: 1. How hard is this code to change? 2. How hard is this code to delete? 3. How hard is this code to test? Code that is hard to change, delete, and test is bad. Code that is easy to change, easy to delete, and easy to test is good. (Note: you can't easily change code if you can't understand it) Long vs short functions don't really matter. Objects vs functions don't really matter. It doesn't matter if you use or don't use a ton of abstractions if they are easy to delete and change.