4 ms·
Good naming, often a causality in older codebases, helps this a lot. Abstraction hurts when things are abstracted and named poorly but helps a massive amount w
by barefoot 8y ago
Good naming, often a causality in older codebases, helps this a lot.
Abstraction hurts when things are abstracted and named poorly but helps a massive amount with readability when done well.
If I’m looking at a (software) method to cook a meal show me the high level salient features of that process in some centralized place. Don’t show me the angle of the knife blade or the exact movements used to chop the tomatoes. It should be (simplified in many ways):
PreheatOven(temperature:400);
ChopVegetables();
CombineIngredients()
Etc...
Sadly the problem is this basic level of software abstraction is preserved in much enterprise software but ChopVegetables upon closer inspection - and after drilling down into other methods - will also purchase missing ingredients and if you don’t have the money to buy them will also sell your items including the already preheated oven you need to cook the food.
- rumcajz 8y agoThat's the cost of abstraction. Abstraction done well is great. But "done well" means specifying the behaviour, all the quirks and corner cases, documenting it and keeping the docs in sync with the code. That's prohibitively expensive. It's only done, to some extent, for system libraries. So we end up with a lot of badly-executed leaky abstractions. It's a cost problem and with the current economics of software development I see nothing we can do to fix it. But at least we can avoid unnecessary abstraction: Is this function called only from a single place? Yes? Then get rid of it.
- zoul 8y agoBut at least we can avoid unnecessary abstraction: Is this function called only from a single place? Yes? Then get rid of it. To me it makes perfect sense to have a function that’s only called from a single place. If the function represents a coherent abstraction (ie. decodeDateStamp, not continueCreatingUser2), then it makes the code easier to read.
- Retra 8y agoIf it's only called in one place, it should be easier to optimize as well, because the context of the call is clear.
- Benjammer 8y agoIf it's only called from one place, why not inline it and write a comment? Even just inlined with only the function name you were going to create, as a comment, would be easier to read than a function call to another file. How is that worth the overhead when the function only has one call site?
- sideshowb 8y agoBecause bugs per line increase when functions get longer than one screen fold. As well as reducing the conceptual length of your function, bundling up part of it in another function called only once makes a statement, possibly even some guarantees about which variables are and aren't modified by the sub function. It may also decrease the number of variables used in your function and hence the risk of misusing one.
- MaulingMonkey 8y agoTaking this logic to the extreme, several languages let you define functions within other functions. Since everything is ultimately just used by main, why not define everything in main? Well, those local functions usually capture context too, so every local variable of main is now effectively global - with all the difficulties of reasoning about widely shared global state that brings. On large codebases you now have a main function that's millions of lines of code - a bit hard to reason about. In a less extreme example, I more commonly I see functions in the several thousand lines of code range. John Carmack has actually argued for this "style C" at least in some cases: http://number-none.com/blow/john_carmack_on_inlined_code.html http://number-none.com/blow/john_carmack_on_inlined_code.htm... . When it's a simple linear flow like those examples, I'm even relatively OK with that style! But IME clean examples like that are the exception, not the rule. Most functions with thousands of LOC have very high cyclomatic complexity scores too, and are hard to reason about. I have significant trouble keeping track of invariants and edge cases in such code. Ultimately, it can be done, but you have to figure out how to digest such code into bite sized chunks of functionality and what they operate on. Functions declare in code those chunks, imposing some limits on how they interact with other chunks - only interacting through global, member, or input variables. You're forced to give them names, which are much better maintained than the section comments. OTOH - 1 line functions used in one place? I'm quite willing to inline those. And larger functions. There's a sweet spot to cater to - something about how much code you can reasonably keep in your head and reason about all at one time.
- deleted 8y ago[deleted]
- hliyan 8y agoQuite serendipitously: https://twitter.com/h_liyan/status/972475860660842496 https://twitter.com/h_liyan/status/972475860660842496 "Programming is like cooking: in Python, you use pre-made bolognese sauce; in C++, you start from fresh tomatoes and minced meat; in Assembly, you have a farm where you grow your tomatoes and raise your cow." - @gv_barroso "In #Java, first you think about Cuisine, then ItalianCuisine, then you think of Bolognese as a specific case of Sauces, which is in turn a more specific case of all things with an EdibleInterface, and whether Tomato is a Fruit or Vegetable. In the end, you die of hunger."
- Nursie 8y agoWhile I like the humour a lot... I don't think Java is what is was about 8-15 years ago. I did a little at university in the late 90s, and have started working with it professionally just in the last three years. AFAICT the enterprise OO nightmare is over, and it's much more like working with python - pick some components and libraries that implement the details of what you need then build your business logic. With lambdas, streams, futures etc, it becomes quite straightforward and powerful.
- SquishyPanda23 8y agoModern Java has a lot of features borrowed from functional programming, but using them is still pretty awkward and verbose. Kotlin is vastly better in this regard IMHO.
- delusional 8y agoThat depends a lot on where you end up. We're still doing Enterprise OO, and it's still very much alive.
- adwf 8y agoWhilst Java the language has progressed, Enterprise OO is sadly far from dead :(
- edflsafoiewq 8y agoI've noticed that in order to solve "what abstraction should I use here" it is often enough to solve "what should the name of the abstraction be". I suppose this is because a name essentially sets up an analogy and if you've picked the correct analogy you can freely exchange everything you already know between the analogized domain and your problem domain.
- jacobolus 8y ago> often a causality in older codebases Did you mean casualty?
- barefoot 8y agoYes I did. This was typed out on a mobile phone and missed that one. Now I can't edit it.
- gilbetron 8y agoI love when developers make food analogies, they almost always show their ignorance of cooking, and sometimes of software development. After 30+ years of software development, I always read the actual code to find out how software works. "CombineIngredients" is useless and honestly quite horrific. Instead of "simplifying" it is obfuscating. The details of cooking are in the function. I don't need to see "PreheatOven" at the same level as "CombineIngredients". One of my favorite posts on this subject is: http://number-none.com/blow/john_carmack_on_inlined_code.html http://number-none.com/blow/john_carmack_on_inlined_code.htm... Tucking away code in a function serves a purpose, and that purpose is not to make the highest level function hold no detail. Like cooking, software is about the details, so don't hide them from me. I can't stand the "Russian Doll" approach of a function that just calls another function that just calls another function that just ... Nothing improves the understandability of 10 lines of code than spreading it across a 10 functions often in different files /s Great abstractions exist, but very few abstractions are great.
- Bekwnn 8y agoIn my experience Unreal Engine 4 suffers from this. It's a fantastic game engine and in many ways its code is really great, but on many of the occasions when I've had to trace call stacks in its source, it's a mess of long function call chains marred with branching conditions and extremely similar sounding function names. I think it's a good example of how code can look clean but actually be a bit of a mess to work with. In contrast at my job I've been making heavy use of structs and functions scoped solely to cpp files. The result has been in my experience somewhat messy-looking code which actually has very low cognitive overhead and is very easy to change. Sometimes long functions are a viable answer. It all depends on whether it actually makes sense to tear things out, otherwise that function called in only one place you pulled out is just polluting the scope. There are other ways to handle it: local function, placing the chunk of code in its own scope limiter, and possibly comments documenting and breaking down the steps.
- charleslmunger 8y agoYes. For many codebases, the primary purpose of the source code is being read by humans with the goal of understanding its actual behavior. When I'm reading through a codebase obfuscated by single-use helper functions, the first thing I do is inline them all. Bugs often result from the software doing something different from what its author intended, so the human-authored function name is not a substitute for reading its implementation.