TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Avoid Indirection in Code

263 点作者 kevlar1818将近 6 年前

67 条评论

gitgud将近 6 年前
It&#x27;s not <i>indirection</i>, it&#x27;s <i>bad abstraction</i> that&#x27;s the real issue here. Consider the example used in the article:<p><pre><code> if x.startswith(&quot;foo&quot;): do_something_with(x) if is_foolike(x): do_something_with(x) </code></pre> The problem with both of these variations is that the &quot;if-statement&quot; doesn&#x27;t have any meaning behind it. There&#x27;s no gain in the indirection presented here. Whereas the following code has meaning:<p><pre><code> if checkHasPermissions(x): do_something_with(x) </code></pre> The reader of the function is now able to understand the <i>purpose</i> of the indirection and the function has clear responsibilities.<p>Remember although code is interpreted by machines it&#x27;s <i>read</i> by humans...
评论 #20261620 未加载
评论 #20263434 未加载
评论 #20260927 未加载
评论 #20261003 未加载
评论 #20260903 未加载
评论 #20261284 未加载
评论 #20268333 未加载
评论 #20261815 未加载
评论 #20261475 未加载
UglyToad将近 6 年前
While the example in the article isn&#x27;t great for making the point I actually agree with the main idea.<p>I understand the arguments for &#x27;Uncle Bobifying&#x27; code but I think, as the article says, there&#x27;s a balance to be struck. It&#x27;s highly likely the next time I see your code (or my code if I&#x27;m coming back to it a month or two later) is when I need to fix a problem with it. While it&#x27;s useful to have it split up into logically discrete chunks with sensible naming these 2-4 line functions far increase the mental load of the code.<p>Given I&#x27;m debugging the code because there is presumably a problem with it I can&#x27;t trust these sensibly named functions to do what they say, I need to check their internals to reason about changes in application state. Though well named functions at least help the maintenance programmer reason about the why they&#x27;re equally liable to code rot as the comments they replace.<p>As in all things there&#x27;s a balance to be struck but I&#x27;d rather see a 20-30 line method than jump between 5 files because someone has read Clean Code recently.
评论 #20261119 未加载
评论 #20261295 未加载
评论 #20261824 未加载
评论 #20261115 未加载
评论 #20263466 未加载
quasi将近 6 年前
The point made by the article (avoid using due to problems for debugging and code review etc.) does not hold much water. &#x27;Indirection&#x27; can be invaluable while separating &#x27;how you got it&#x27; from &#x27;what to do with it&#x27; (it=some data), for example. &#x27;what you do with it&#x27; can remain unaffected by changing &#x27;how you got it&#x27;. This will not mess with code review practices but make understanding the pieces easier. It will also make debugging easier as you can test both those pieces independently. What the OP calls indirection is just bad code or meaningless abstraction.<p>Zed Shaw puts it well: &quot;Abstraction and indirection are very different yet cooperating concepts with two completely different purposes in software development. Abstraction is used to reduce complexity. Indirection is used to reduce coupling or dependence.&quot;
评论 #20260955 未加载
评论 #20261519 未加载
评论 #20261102 未加载
评论 #20263555 未加载
keyle将近 6 年前
That&#x27;s a pet peeve of mine; I see it all the time when I work with lesser experienced developers, only I didn&#x27;t know how to call it.<p>I call it onion skin development, where the developer keeps hiding stuff in more layers of the onion, making my eyes water as I have to dig deeper and deeper to essentially find `a.foo(b)` under 12 layers of abstraction.<p>They&#x27;re so focussed on making everything look so purrty, they forgot that it&#x27;s about telling the computer to do something, as clearly as possible.
评论 #20260472 未加载
评论 #20260562 未加载
评论 #20260458 未加载
评论 #20262339 未加载
janpot将近 6 年前
Ok, to take a real world example instead:<p><pre><code> if (url.startsWith(&#x27;http:&#x2F;&#x2F;&#x27;)) { </code></pre> vs.<p><pre><code> if (isAbsoluteUrl(url)) { </code></pre> If the next developer comes by in 2 months to fix the case for <a href="https:&#x2F;&#x2F;" rel="nofollow">https:&#x2F;&#x2F;</a> urls (and protocol relative ones in 6 months), they&#x27;ll immediately be able to spot the intention of the code and can easily fix it in the abstraction layer that&#x27;s already in place. Moreover, the fix will be applied every other place this faulty assumption about their input was made.
评论 #20267727 未加载
评论 #20268795 未加载
评论 #20262227 未加载
sudhirj将近 6 年前
The argument in the post is a bit of a strawman - for really simple examples like that indirection makes no sense. Saying &quot;is_something_like&quot; as an alias for &quot;starts_with&quot; is an unnecessary alias that does nothing for the readability or duplication of the code. Indirection really isn&#x27;t necessary in this case, even if you use &quot;starts_with&quot; in a thousand places.<p>It might make sense if you&#x27;re trying to do a comparator, like &quot;is_email_equivalent&quot; - in that case the indirection would make sense - you might be doing a just a lower case check today, but in the future you might expand that to take into account Gmail specific equivalence rules.<p>For stuff like &quot;read_json_from_file&quot;, indirection makes a lot of sense. No point doing the whole &quot;create_buffer&quot;, &quot;read_file_to_buffer&quot;, &quot;parse_json&quot; deal every single time.
评论 #20260635 未加载
评论 #20260683 未加载
评论 #20260643 未加载
a_t48将近 6 年前
I really disagree with just the toy example. If it&#x27;s just in one place, fine, but when you need to change the example to also do something else like check the end of the string `return x.startswith(&quot;foo&quot;) &amp;&amp; !x.endswith(&quot;bar&quot;);` this style will end up biting you. Yeah, your IDE has tools that can help, but you might not catch all examples, especially in other branches.
评论 #20260774 未加载
评论 #20260665 未加载
keithnz将近 6 年前
I don&#x27;t think the author made a very good case for this. The case made on readability. But indirection is very common that if you can&#x27;t read a piece of code without having to drill into the implementation of every function, lifes going to be quite painful. Depending on your language and editor &#x2F; IDE, seeing implementation is often trivial. The only case, I see, for inlinig is where the pieces of code are very cohesive and tied together and will only be used as a single unit. Maybe that&#x27;s what the author was trying to get at, don&#x27;t break atomic units of code up.
评论 #20260396 未加载
gregmac将近 6 年前
First of all, one of the biggest reasons to do this (and not mentioned) is unit testing. If there&#x27;s several variations of input, it can simply testing because it&#x27;s easier to write tests for smaller units of code (fewer or no mocks, and fewer parameters). This alone is probably enough to override any negative, in my opinion.<p>Secondly, and in contrast to the point of the article, it can usually help readability.<p>If you&#x27;re wrapping standard library functions, then I agree, the examples in the article are bad candidates. If you&#x27;re dealing with something where magic values are involved, for example, then something like the following can <i>greatly</i> help readability:<p><pre><code> &#x2F;* Checks if a given user id was generated by the legacy system. These are either 5 digits numeric, or start with &quot;B&quot; or &quot;L&quot;. *&#x2F; is_legacy_user(userid) </code></pre> This method body can include the details about the strange legacy naming or links to documentation, and the code consuming this can focus on the logical implications without caring about these details.<p>Put together, the code flow is easier to follow, and if you&#x27;re in doubt about the method you can check out the unit tests to see what situations exist that might not be obvious at first glance.<p>Edit: actually, I take that back. Even if you&#x27;re just wrapping a standard library call, then there&#x27;s really no issue so long as there&#x27;s a domain reason (eg, is_legacy_userid() vs is_userid_fivedigits()), and there&#x27;s unit tests. The unit tests can prove the standard library function is all that&#x27;s needed.
评论 #20260859 未加载
评论 #20262956 未加载
xxxpupugo将近 6 年前
Highly recommended this book:<p>A Philosophy of Software Engineering:<p><a href="https:&#x2F;&#x2F;www.amazon.com&#x2F;Philosophy-Software-Design-John-Ousterhout&#x2F;dp&#x2F;1732102201" rel="nofollow">https:&#x2F;&#x2F;www.amazon.com&#x2F;Philosophy-Software-Design-John-Ouste...</a><p>This is mentioned as one of the symptoms&#x2F;appearance of bad code: Shallow&#x2F;Passthrough methods, which results deep call chains, adding up cognitive overload. The author in this book recommends deep module over shallow module, which tends to end up with more cohesive code.
mannykannot将近 6 年前
In theory, this abstraction of small details is not a problem, but in cases like this, I would prefer to see the inlined version, simply because, in many languages, a function call raises the possibility that it has side-effects that you might want to know about. In code that is abstracted to the max, that is sometimes a difficult question to answer, even if your IDE lets you easily trace the call stack as you read.<p>There is a related problem that causes more trouble, but it cannot really be blamed on the initial abstraction. It goes like this: you have something, such as a foo-ness test, that is needed in several places, so you abstract it as a function for all the right reasons. Later on, one of those uses changes, and needs slightly different logic than the other cases. Instead of writing a new function (that may, as part of its implementation, call the original), the person making this change (not you, of course!) modifies the original function to handle both cases. This, by itself, makes the code more complex than necessary, and it gets worse if the criterion for which path is taken in the function is not something that has any significance for someone who arrived at this function through reading the code for one of the other cases that call it. And if the new path has a side effect, now all the other cases are also calling a function that potentially has side effects...<p>This is how code rots.
rocky1138将近 6 年前
This could easily be remedied by our editors offering a keystroke to inline the function definition in a smaller font and different colour so that we may read it, in order, as though it were one big file. Once the gist of the function has been understood, another keypress could take it away.<p>Visual Studio has this and it&#x27;s called &quot;Peek at function&quot;.
评论 #20260619 未加载
ubercode5将近 6 年前
In general I disagree. In large codebases you&#x27;re going to have a number of abstractions that you&#x27;ll have to deal with by their nature. Flattening these can cause a scenario where you have a long function with clusters of lines of code organized in a block are doing different functionality that isn&#x27;t clearly stated. This may include a comment at the start of each block discussing what the next few lines are doing. And without abstraction, the functionality later may not be cleanly decoupled. Debugging these types of functions also adds significant mental load trying to see the forest through the trees.<p>From the article it seems like the bigger issue might be debugging style. For me, when debugging those abstracted functions should be treated and trusted as black boxes until you have sufficient reason that function is the issue, and then dig into it and ignore the rest of the function. Trying to build a mental model by effectively flattening every function takes huge cognitive load, and only is feasible for small amounts of code. Once the codebase is large enough it&#x27;s going to become impossible to do this.
JackFr将近 6 年前
&quot;Avoid Indirection in Code&quot; is a foolish thing to advocate, and the example provided is more confounding than explanatory.<p>Abstraction is the key. Abstraction can be used to reduce incidental complexity by hiding an implementation. Abstraction can be used to reduce the cognitive load of the essential complexity by making the code look like the problem domain.<p>But there is a cost to abstraction. A person reading your code may not have the same mental model of the problem, and might’ve be working at a higher or lower level of abstraction, in which case the &quot;indirection&quot; might be a stumbling block. Weigh these costs carefully.
reuben_scratton将近 6 年前
John Carmack&#x27;s thoughts on this topic are worth a read: <a href="http:&#x2F;&#x2F;number-none.com&#x2F;blow&#x2F;blog&#x2F;programming&#x2F;2014&#x2F;09&#x2F;26&#x2F;carmack-on-inlined-code.html" rel="nofollow">http:&#x2F;&#x2F;number-none.com&#x2F;blow&#x2F;blog&#x2F;programming&#x2F;2014&#x2F;09&#x2F;26&#x2F;carm...</a>
cpeterso将近 6 年前
See also Zed Shaw&#x27;s &quot;Indirection Is Not Abstraction&quot;. The blog post is a bit overlong, but Shaw&#x27;s point is important:<p><i>Abstraction and indirection are very different yet cooperating concepts with two completely different purposes in software development. Abstraction is used to reduce complexity. Indirection is used to reduce coupling or dependence.</i><p><a href="https:&#x2F;&#x2F;zedshaw.com&#x2F;archive&#x2F;indirection-is-not-abstraction&#x2F;" rel="nofollow">https:&#x2F;&#x2F;zedshaw.com&#x2F;archive&#x2F;indirection-is-not-abstraction&#x2F;</a>
bcheung将近 6 年前
There&#x27;s nothing wrong with this kind of &quot;indirection&quot;, I agree there are times when you might not want to do this but I would put it in terms of cognitive overload.<p>By substituting a series of lower level concepts&#x2F;tasks&#x2F;procedures into a single keyword the reader can more easily think about the problem domain. That&#x27;s a good thing. If they know the abstraction, it reduces the number of things the reader must keep in their head.<p>However, if the reader is not familiar with the abstraction they will need to spend time to familiarize themselves. This can break down when trying to understand an abstraction leads to the &quot;endless rabbit hole&quot; problem. Basically, you have to open another file to see how that abstraction works, then it uses some other abstraction which you need to open another file to read, and so on. If the depth is too great the reader will lose context and won&#x27;t be able to fit the problem in their head.<p>To avoid this, pull more of the functionality into the function instead of outsourcing it to an abstraction. Favor writing code that reads sequentially rather than as a series of jumps through a bunch of files.<p>As always, this is largely a matter of taste so use your best judgement and balance abstraction with pragmatism.
admax88q将近 6 年前
Yeah avoid indirection. Instead of<p>&gt; x.start_with(&quot;foo&quot;)<p>do<p>&gt; x.length &gt;= 3 &amp;&amp; x[0] == &quot;f&quot; &amp;&amp; x[1] == &quot;o&quot; &amp;&amp; x[2] == &quot;o&quot;<p>Now the reviewer doesn&#x27;t have to jump to the definition of &quot;starts_with.&quot; Isn&#x27;t that way more readable?
sbov将近 6 年前
It really depends.<p>If you repeat this a lot, you might have a domain concept. If you can come up with a name that makes sense for your domain then you should probably take that domain concept and realize it within your code in the form of some kind of abstraction - e.g. a function call. Even if it is as simple as a .startswith call.<p>If you can&#x27;t come up with a name, kinda depends upon how prevalent it is. If you just do it a handful of times, no big deal.
carapace将近 6 年前
Err, this isn&#x27;t <i>bad</i> advice, just kinda junior-level.<p>First, if you&#x27;re defining interface indirection to support <i>encapsulation</i> and you wind up <i>never</i> rewriting (or adding another) implementation behind that &quot;external function&quot; or method, you <i>probably</i> didn&#x27;t need it. (YAGNI, yo?)<p>Second, if you&#x27;re defining interface indirection and it makes it <i>harder</i> to understand the code, you&#x27;re doing it wrong anyway.<p>- - - -<p>Indirection that hurts is like when you do dispatch through a dict of functions and now all your tools, debugger, etc. can&#x27;t tell you about the static structure of your code:<p><pre><code> dispatch = { &#x27;name&#x27;: handler_func, ... } ... dispatch[selector](*foo, **bar) </code></pre> That kind of thing seems like a good idea but:<p>&gt; &quot;Everyone knows that debugging is twice as hard as writing a program in the first place. So if you&#x27;re as clever as you can be when you write it, how will you ever debug it?&quot;<p>–Brian Kernighan, &quot;The Elements of Programming Style&quot;, 2nd ed., chapter 2<p>(Also, notice that, in Python at least, derp.baf(<i>foo, </i>*bar) is the same thing, eh? EH??)
评论 #20264867 未加载
zimbatm将近 6 年前
It&#x27;s more of a problem with OOP language because the function call might hide a side-effect. So now the reader has to go and read the function definition to make sure no side-effect is happening in there.<p>With functional languages the mutable data is available in the reader&#x27;s context. The implementation might still be mysterious but hopefully the function name is good enough to give a clue about that.
评论 #20261781 未加载
Thunderya将近 6 年前
This made me think: is there an IDE or plugin that makes it easy to visualize the source code of a function by inlining the sources of the undercalled functions?<p>For example, before the transformation:<p><pre><code> function isFooLike(x) { return x.startsWith(&quot;foo&quot;); } function isBarLike(x) { return x.startsWith(&quot;bar&quot;); } function bigFunction(str) { if(isFooLike(str)) { console.log(&quot;Foo!&quot;); } else if(isBarLike(str)) { console.log(&quot;Bar!&quot;); } else { console.log(&quot;Unknown!&quot;); } } </code></pre> And then:<p><pre><code> function bigFunction(str) { if((function(x) { return x.startsWith(&quot;foo&quot;) })(str)) { console.log(&quot;Foo!&quot;); } else if((function(x) { return x.startsWith(&quot;bar&quot;) })(str)) { console.log(&quot;Bar!&quot;); } else { console.log(&quot;Unknown!&quot;); } }</code></pre>
评论 #20262925 未加载
asavinov将近 6 年前
Indirection [0] is a quite big topic and applying it can result in both significantly improving the code and getting more problems than benefits. Therefore, the question is when indirection is appropriate. One opinion [1] is that<p><pre><code> &quot;We can solve any problem by introducing an extra level of indirection&quot; </code></pre> Another opinion is expressed in this post (avoid indirection). In the context of this example, indirection is related to the problem of modularization and separation of concerns. In particular, a typical question is whether we want to hide some details or use the functionality <i>directly</i>.<p>[0] <a href="https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Indirection" rel="nofollow">https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Indirection</a><p>[1] <a href="https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Fundamental_theorem_of_software_engineering" rel="nofollow">https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Fundamental_theorem_of_softwar...</a>
评论 #20266761 未加载
husainalshehhi将近 6 年前
Well, indirection can be quite helpful even for simple checks:<p><pre><code> if is_json(somestring) { &#x2F;&#x2F; do something } function is_json(string s) { return JsonLib.convert(s) != NULL } </code></pre> this indirection is easier to read rather than thinking too much about what JsonLib.convert(s) != NULL mean. This does not hinder debugging because you only debug this is_json function when the code does not evaluate is_json as expected. Every other time, you debug what&#x27;s in the if statement.<p>The given example x.startswith(&quot;foo&quot;) is simple enough to include it directly. However, if there is a business reason for why &quot;foo&quot; is checked here, it will be difficult to understand why the check is there. This will be more difficult to read a code like this:<p><pre><code> if x.startwith(&quot;-&quot;) do something </code></pre> instead of<p><pre><code> if is_yaml_node(x) do something def is_yaml_node(x) x.startwith(&quot;-&quot;)</code></pre>
DarkWiiPlayer将近 6 年前
My mental benchmark for the cost&#x2F;payoff of indirection is usually<p>&gt; Would this code be easier to read if A) I inlined the code or B) I replaced it with a descriptive function name.<p>Function could be read as Class or Method if you&#x27;re dealing with OOP.<p>One important criterion for this is that other programmers already know the language, not your program. Writing your own function can help to put a meaningful name on a set of behaviors, but it also means the introduction of a new specific set of behaviors the reader needs to keep in the back of their mind. It&#x27;s kind of similar to Jakob&#x27;s Law [1] in a way, but applied to source code. (In a way, source code readability in&#x27;t all that different from UX, if you think about it)<p>[1] <a href="https:&#x2F;&#x2F;lawsofux.com&#x2F;jakobs-law" rel="nofollow">https:&#x2F;&#x2F;lawsofux.com&#x2F;jakobs-law</a>
tawy12345将近 6 年前
I think some commenters don&#x27;t understand the challenges of reviewing code contributions as an open source maintainer. Many people already feel like they&#x27;re doing you a favour by contributing any code. This is amplified if they&#x27;ve followed &quot;best practices&quot; and broken it down into many tiny functions and objects. As a maintainer, having to review and maintain code that is abstracted in the wrong way is a major headache. Trying to tell someone that their code is over-abstracted risks starting a debate on the code review and generally derailing the process.<p>An article like this at least sets some precedent and gives you something to point to.
评论 #20265252 未加载
neilwilson将近 6 年前
This is back to the usual intractable problem - naming things. If you name the function properly and it abstracts the operation of the function precisely then you have added information, not taken it away.<p>You understand what &#x27;has_kettle_boiled&#x27; means.<p>Inlining functions makes it difficult in a sequence to work out what are the separate steps. Splitting those out into a function - particularly if they take the temporary variables with them - makes it easier to understand what the higher level function does, not harder.<p>Perhaps we should revisit stepwise refinement as a technique and the need for functions to be loosely coupled and highly cohesive.
评论 #20263859 未加载
geodel将近 6 年前
I don&#x27;t know much about Python programing. In Java though I deal with ludicrous amount of indirection and &#x27;design pattern&#x27; turdlets which deeply hurt code comprehension everyday.<p>Just last week I was handed a project with single functionality: &#x27;Doing ldap bind on receiving userid&#x2F;password over http and return success&#x2F;failure&#x27;. This project has about 40 Java files in 18 directories. As far as enterprise projects go it follows all best practices of Java and Micro services etc. But I find this project absolute turd and hopeless to refactor. A clean rewrite would be only sane thing.
mgerdts将近 6 年前
Reminds me of my first day of python performance analysis, which led to this fix.<p><a href="https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20130227000541&#x2F;http:&#x2F;&#x2F;defect.opensolaris.org&#x2F;bz&#x2F;show_bug.cgi?id=11002" rel="nofollow">https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20130227000541&#x2F;http:&#x2F;&#x2F;defect.ope...</a> <a href="https:&#x2F;&#x2F;github.com&#x2F;oracle&#x2F;solaris-ips&#x2F;commit&#x2F;53bb14f750dd1111bf8938278c2436e4141798dd" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;oracle&#x2F;solaris-ips&#x2F;commit&#x2F;53bb14f750dd111...</a>
DubiousPusher将近 6 年前
There&#x27;s a lot of reasons to avoid indirection but I don&#x27;t think decreasing the number of files you have to navigate is one of the better ones.<p>I mean, who reads code linearly anyway. My hand hovers over F12 (the shortcut for go to definition in Visual Studio) half the day. Any good IDE will help with this.<p>I feel like this person is like, I don&#x27;t want to drill all these holes to build my deck. I&#x27;ll use nails. And it&#x27;s like, &quot;dude, somebody invented the 3 inch self tapping deck screw; go to town.&quot;
评论 #20260906 未加载
rileymat2将近 6 年前
&gt;However, there is also a cost to this behavior. When a new reader encounters this code, they need to jump between many function definitions in many files. This non-linear reading process requires more mental focus than reading linear code.<p>I think this is the problem right here. As a new reader you should first trust the function name does what it says it does and continue linearly. Later on, if you need to go back and look at the details or where the function name misleads or does more than it says.
roland35将近 6 年前
There are a lot of examples of heavyweight indirection in Hardware Abstraction Layers (HAL) on microcontroller vendor code - ST is one of the worst offenders! In their sensor code library I counted 9 layers deep of functions to simply send a command to a magnetometer over I2C.<p>I find this code hard to work with since without actually debugging the code there is no easy way to just click on a function and see what is called - a lot of functions reference a data structure of I2C function pointers...
lriuui0x0将近 6 年前
I agree with the conclusion largely, but I think the reasons listed in the article are not good. Personally I feel the problems of doing too much abstraction are belows.<p>1. It takes a lot of time.<p>2. You might end up with bad abstration that in the end hinders you. You then have to work around the abstration you created.<p>3. More functions create more entries you need to understand in your mind, this can sometimes make code harder to reason about.
vast将近 6 年前
The main point here is worth a broader discussion. Part of the problem is that we tend to hide behind mental concepts that a ambiguous, incomplete or just bad.<p>DRY is a awful &quot;thing&quot; (a decree at best). In the worst interpretation it simply says &quot;never write the same code twice&quot;. There is no balance or end to it. It doesn&#x27;t have a competitor or alternative. It somewhat implies that it is always good. It doesn&#x27;t define a scope where&#x2F;when it should be applied. There is simply no broad agreement how to use it in practice. DRY touches multiple concepts, each too complex to put behind three tiny letters.<p>OP fails to make a great point, but the direction is right. We cannot take DRY and &quot;code reuse&quot; laws as granted. Abstractions and indirections have their downsides. It increases systemic complexity and may add dependencies. It certainly limits how easy the full system can be understood by humans.
评论 #20261889 未加载
评论 #20261504 未加载
Rapzid将近 6 年前
Pretty much everything written in Ruby before &quot;Practical Object-Oriented Design In Ruby&quot; by Sandi.. Haha, #KiddingNotKidding.<p>Looking for a non-contrived and&#x2F;or non-scarecrow example? Anything in the Chef code-base circa 2014 or prior.. No clue if it has changed.<p>Ruby code bases were the poster children of DRY-gone-wild.
2rsf将近 6 年前
Like everything else you need to consider the context. I was involved in a rather big internal automation project written in Python, it involved a few platforms and targets and was ran open source style where each team could hook up to the infrastructure independently or make changes to it using PR.<p>Some of the code ran remotely using RPC, so part of the code sat in different repositories.<p>Naturally this called for multiple levels of abstraction and indirection. The results ? a big ugly pile of levels if you are trying to understand what the code does, that led to bugs, unnecessary complexity since it&#x27;s hard to foresee the future and always abstract the way the code will need and the worst was that people simply gave up on trying to contribute do to the complexity of the otherwise simple code.
skywhopper将近 6 年前
As with anything, the key here is to strike the right balance. Sometimes the code behind the ‘if’ test will be worth factoring out and sometimes it’s better to keep it simple. Impossible to say what the line is, as it depends on the team, the language, etc.<p>But in relation to the actual problem of legibility to a new developer still building up the mental model of the code, this is an area where tooling can help. You ought to be able to view the called-method’s body with a keystroke, without losing the current context, in an IDE or a code review tool. I’m sure some IDEs do have such features. Yes this is harder in some languages than others but in general it’s too bad these sorts of features aren’t built in to Github and Gerrit as well.
hyperpallium将近 6 年前
How should we decide whether to abstract something into a function?<p>Abstract aspects <i>likely to change</i>, via interfaces that aren&#x27;t, says Parnas (<i>On the Criteria To Be Used in Decomposing Systems into Modules</i>, <a href="https:&#x2F;&#x2F;www.win.tue.nl&#x2F;~wstomv&#x2F;edu&#x2F;2ip30&#x2F;references&#x2F;criteria_for_modularization.pdf" rel="nofollow">https:&#x2F;&#x2F;www.win.tue.nl&#x2F;~wstomv&#x2F;edu&#x2F;2ip30&#x2F;references&#x2F;criteria...</a>)<p>abstract-unless-leaky: hide information you don&#x27;t need.<p>In practice, writing tests helps me see what is naturally part of a module vs. a usage of it. You want the module to contain everything it needs to do its job, and nothing else. But this assumes the modularization (its &quot;job&quot;).
ojbyrne将近 6 年前
Decomposition and abstraction are fundamental tools of software design, obviously it can be done badly, as can anything. Carried to the logical extreme, this suggests that hiding the complexity of arithmetic operations like + and - is a bad idea.
satyenr将近 6 年前
I don’t quite get the point of article. It essentially boils down to:<p>“Abstractions — or rather, indirections — are bad for for readability, but they are good in some cases.”<p>Umm... this can be said for pretty much anything. Over use of anything is bad. Inappropriate use of anything is bad.<p>&gt; Mostly, I want authors to be aware that there is a human cost to indirection that is felt more acutely by everyone reading the code except the original author.<p>Sure. It’s a well known fact that abstractions have associated costs — nothing new there. However, the author seems to forget that not abstracting things can be equally costly — even when human readability is concerned.<p>Imagine someone trying to figure our the business logic be reading code. All they need to know is some conditions are being met for certain steps to be executed. The actual steps involved in the check are irrelevant to the business logic. The condition can be modelled separately from the business logic and can even change independently. For example:<p><pre><code> def is_foo_condition_satisfied((self): return self.variable.startswith(‘foo’) </code></pre> In this case, it just happens that the satisfaction of a condition is implemented as prefix check. In the future, it might be changed to checking a flag or reading a database, or anything really. If you sprinkle the startswith() check all over you code, it will be hard to make the change. And it actually hurts readability. While reading a large codebase, you want to focus on specific parts of it — getting into the details of everything all at once does not make it easier.<p>The most important thing to note is — programming is not confined to s strict set of rules. It’s not an exact science. You can’t define a strict a rules that must be followed. What makes sense somewhere is completely useless elsewhere. The Linux Kernel is full of goto statements, despite the fact that goto is considered harmful.<p>Dogma Driven Programming must be avoided. Even well known programming practices are guidelines at best. One must evaluate whether a given rule fits a given situation.<p>Coming back to the example at hand, functions are meant to divide the program by the logical tasks being performed, not by number of lines or any other metric. As I said before, it’s more art than exact science.<p>Is the check for foolikeness of something a logically distinct task? It depends on the context. There is no way to write a cardinal rule either way.<p>PS. I sincerely hope no one comes to me for code review with a monolithic function because of all the inlining — citing this article as inspiration.<p>Edit: Typo correction.
ivanhoe将近 6 年前
Having a central place for logic is a HUGE pro, while both those cons apply to a very narrow class of situations: having a super simple condition logic on one side vs. not very smartly named indirection. In real life (at least mine) you&#x27;ll often have a complex or cryptic-looking conditions (requiring that you&#x27;re deeply familiar with business logic, laws, tax rules, etc.), and abstracting them into sensibly named functions actually helps the review process and debugging IMO.
z3t4将近 6 年前
Code is like a bunch of cords. Each time you add something, it&#x27;s like adding another cord, and at some point you will have &quot;spaghetti&quot;, and you pull out every cord, unravel it, connect everything neatly and use zip-ties to hold everything in place. The problem is when you use the wrong abstraction, eg. you start zip-tie:ing before even knowing what other cables to add. So don&#x27;t be afraid of using the copy&#x2F;paste function. Make it easy to unravel.
winkelwagen将近 6 年前
There is one thing that I’m missing, yes when the only thing you do is wrapping the standard library with a function it’s overkill. But when there is a business decision logic behind it I would still advice to extract it. Creating a function with a name tells you something about the why!<p>I think peek definition could help you out when you are really dealing with it in the code base. I just wish it would be smarter. Peeking a function with 5 lines, peek it. If it’s larger, jump to it.
评论 #20260835 未加载
rajangdavis将近 6 年前
I feel as though this is a huge part of working with Rails as there is an incredible amount of magic that happens under the hood.<p>From my limited experience, I think abstractions like these are helpful as long as there is consistency.<p>This is easy to do by yourself but not always on a team; however, I think Rails has a good balance of conventions that allow indirection to be successful as long as the people writing the code use the same patterns and communicate effectively.
k__将近 6 年前
When I started programming, I tried to get functions below one screen height, have one return and use mutations often.<p>Today, I try to do big functions, multiple returns and const where I can. I still use code-blocks sometimes to scope variables.<p>I sometimes end up with a few hundred lines of code per function, but I can usally scroll over it and find what I need without jumping around.<p>Especially in JavaScript the addition of const and async&#x2F;await helped much with this.
tobr将近 6 年前
It’s not just a problem for review and debugging. I think it’s even worse for refactoring and new feature development, because it makes it so much more difficult to know where to make a change. If the condition in the (silly) example needs to change, should you write a new function to replace the call to is_foolike, should you rewrite its implementation, or should you add a parameter that alters its behavior?
taneq将近 6 年前
One very important reason to avoid this kind of unnecessary indirection is that it wrecks locality of reference for anyone reading it. They end up spending all of their mental energy on skipping around the codebase trying to follow the thread of execution, making it very hard to assemble a coherent picture of what the code&#x27;s actually trying to do.
评论 #20262947 未加载
ncmncm将近 6 年前
The simple rule is: every line of code has to earn its keep.<p>Sometimes we really do need a one-line function to implement a common interface. Sometimes we need it so that, in the two places it&#x27;s called, both get the new behavior when it changes.<p>But abstraction for abstraction&#x27;s sake is just stalling. The technical term is &quot;ratiocination&quot;. It&#x27;s a filthy habit.
评论 #20260868 未加载
shtolcers将近 6 年前
On the other hand fixing &quot;is_foolike&quot; will be pain to fix if used in many places. And - &quot;This non-linear reading process requires more mental focus than reading linear code.&quot; - isnt&#x27;t this a god thing? Good abstraction requires a bit more mental involvement and much less monkey-job when fixing the code.
wawhal将近 6 年前
I slightly disagree because I prefer the code to be consistent all across my codebase.<p>Sure it is some effort to backtrack to the util functions, but I think the effort is worth it because I would rather have a developer spend multiple hours debugging and figuring out the code than having something that is a nightmare to iterate upon.
cryptica将近 6 年前
I agree with OP, if a function just returns the result of another function without any additional processing then it usually means that one (or both) function&#x27;s name does not accurately describe what it does.<p>The name of a function should abstract away from &quot;how it does something&quot; not &quot;what it does&quot;.
CodiePetersen将近 6 年前
If you have defined the function and it is used in many places then you only need to read it once and remember everytime you come across it. How is recoding it every time going to help the readability. I think you just need to make sure the functions purpose is clear either.
vendiddy将近 6 年前
I wonder if the real solution to the indirection is better tooling.<p>If I can&#x27;t easily see the contents of is_foolike, should I stop writing my code like this or should the editor be showing me these details more easily?<p>The real problem: it takes me too much time to see what&#x27;s inside the function.
de_watcher将近 6 年前
&quot;All problems in computer science can be solved by another level of indirection&quot;
XeO3将近 6 年前
Imho, this type of abstraction should be allowed in case of code repetition. So the reviewer&#x2F;debugger will have to go non linear only one time.<p>In any other case the whole code should be in one file.
rb808将近 6 年前
Both are clearly wrong, should have been using proper IoC with a fooCheckerFactory creating a checking object that inherits from a generic interface that is passed in the context.
raiflip将近 6 年前
I wonder how many people who decry abstraction and indirection would choose to work directly in Assembly rather than in higher level languages.
gautam1168将近 6 年前
i regularly see functions that are over 300 lines of code that make no other function calls except ajax. That shit is hard to read.
choiway将近 6 年前
This is an overly simplistic complaint that leads to the argument that you should never use a 3rd party library.
doomjunky将近 6 年前
But <i>all problems in computer science can be solved by another level of indirection</i>. ~David Wheeler
nickthemagicman将近 6 年前
Could it be lack of good tooling?<p>I dont understand why there isn&#x27;t a code editor that will inline functions for you?
Simon_says将近 6 年前
This feels like somebody is testing the limits of what nonsense developers can be made to read.
blibli将近 6 年前
Maybe we should avoid reading code.
unnouinceput将近 6 年前
The connection has timed out<p>The server at www.matthewrocklin.com is taking too long to respond.<p>too many HN readers perhaps?
djohnston将近 6 年前
if the tooling is terrible maybe, but as long as i can click thru to see the referenced method in an IDE it&#x27;s going to be fine
kerkeslager将近 6 年前
Okay, maybe it&#x27;s just a poor example, but in the example used, the problem is <i>definitely</i> not too much indirection.<p>If I was doing a code review and came to this:<p><pre><code> def is_foolike(x): return x.startswith(&quot;foo&quot;) </code></pre> I would comment, but my comment would be, &quot;Could you name this function `starts_with_foo`?&quot;<p>This addresses both concerns mentioned in the article:<p>1. <i>During review, when a reviewer is asked to verify that code is sensible before it can be merged into the main project. That reviewer probably has about a tenth as much time to spend as the original author does on that code.</i> But if the reviewer comes to the code<p><pre><code> if starts_with_foo(x): do_something_with(x) </code></pre> They aren&#x27;t likely to be misled in any way by the indirection. They can just keep reading, without having to look into the implementation of `starts_with_foo`, because either that name is accurate and they know what it does, or they&#x27;ll discover that the name isn&#x27;t accurate when they review the code.<p>2. <i>While debugging future issues. This code will eventually be involved in a bug and some completely different developer will have to glance at this code to figure out what’s going on. They’ll have to understand some small section this code within a few minutes to determine what is relevant. They won’t be able to invest the time to understand the full thought process behind it, and a web of function definitions can slow down this process considerably.</i> But again when they read the code, a good name means they can understand what the code does without having to read the implementation.<p>I do think there&#x27;s an argument to be made that indirection is a problem here, but it&#x27;s a small problem compared to the enormous problem that `is_foolike` is a <i>really</i> bad name for that function.<p>General rules for when to NOT pull something out into a function:<p>1. If it the function call wouldn&#x27;t be clearer than the code (even a better name like `starts_with_foo(x)` loses some information contained in `x.startswith(&#x27;foo&#x27;)`, and the latter is readable enough that there&#x27;s no real upside to the former. If the latter were even two lines long, it would become a lot more worth it.<p>2. If there&#x27;s no repetition. Two similar pieces of code aren&#x27;t enough: you don&#x27;t understand from two use cases what pattern you&#x27;re abstracting, so the result is just going to be a leaky abstraction. Three repeated pieces of code seems to be the sweet spot: now you have enough examples to know what&#x27;s actually repeated, what should be arguments to the function, etc.[1]<p>[1] <a href="https:&#x2F;&#x2F;blog.codinghorror.com&#x2F;rule-of-three&#x2F;" rel="nofollow">https:&#x2F;&#x2F;blog.codinghorror.com&#x2F;rule-of-three&#x2F;</a>
评论 #20260654 未加载
评论 #20261221 未加载
james_s_tayler将近 6 年前
TL;DR - don&#x27;t pointlessly wrap functions from the standard library in methods with non-standard names.<p>Yes. Please. Stop. Doing. That.
评论 #20260617 未加载