Live data from Hacker News

Avoid Indirection in Code

matthewrocklin.com

31–40 of 220 posts

Re: Avoid Indirection in Code

#31
Highly recommended this book:

A Philosophy of Software Engineering:

https://www.amazon.com/Philosophy-Software-Design-John-Ouste...

This is mentioned as one of the symptoms/appearance of bad code: Shallow/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.

Re: Avoid Indirection in Code

#32
post #22
post #13

The argument in the post is a bit of a strawman - for really simple examples like that indirection makes no sense. Saying "is_something_like" as an alias for "starts_with" is an unnecessary alias that does nothing for the readability or duplication of the code. Indirection really isn't necessary in this case, even if you use "starts_with" in a thousand places. It might make sense if you're trying to do a comparator,…

In Python it's json.load(file), so no indirection needed whatsoever.

That is indirection though. json.load basically calls fp.read() and then passes that value to be deserialised.

Everything is indirection.

Re: Avoid Indirection in Code

#33
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.

Re: Avoid Indirection in Code

#34
The simple rule is: every line of code has to earn its keep.

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's called, both get the new behavior when it changes.

But abstraction for abstraction's sake is just stalling. The technical term is "ratiocination". It's a filthy habit.

Re: Avoid Indirection in Code

#36
post #16

I really disagree with just the toy example. If it'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("foo") && !x.endswith("bar");` 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.

I disagree with the toy example even if it’s just 1 use (although to be honest, you could construct a different example where I would choose to abstract only if there were multiple uses).

The big problem I have with the toy example is that both pieces of code convey different ideas to a human reader, and limiting myself to the situations the author finds the indirection cumbersome, such as code reviews, or debugging, both would lead the reader in different directions. The suggested solution would make a reader think the business requirement of that code is to check if the input starts with foo, whereas the indirection code suggests to the reader that the input should be like foo (which in this case happens to be by testing it starts with foo, but that may be incorrect, or may change).

I think the author’s example really hurts their case. A better example may be where the intention indeed is to check whether the input starts with the string foo, but you want to trim the sample first.

I’ve seen code that would abstract that out with a function trimmedStartsWith, or something, instead of just input.trim().startsWith(foo), where I think the former only makes sense if you’re doing it a bunch of time.

Re: Avoid Indirection in Code

#37
post #27

Earlier quoted context omitted.

I’d hesitate to call the good abstractions you describe indirection. The term is most appropriate when describing opaque abstractions that force you into their inner workings in order to understand their purpose. In the original example, what exactly it means to be “foo like” is not obvious. You need to bounce into the other file/method in order to understand it’s purpose. In good abstractions, like the ones you desc…

It doesn't matter how good your abstraction is, if someone needs to know the details it is at that point indirection. And there will always be some cases where someone needs to know the details. The better your abstraction, the less often. But someone, sometime, will still need to look at them.

True, there will always be people who need to know the details. That applies all the way down to CPU opcodes.

I’m making more of a semantic point than anything else. If you define indirection as anything which causes you to jump from the code directly in front of you at any point, then yes, you can call even good abstractions indirection.

I’d argue that such a definition makes the word “indirection” must less useful, as it becomes pretty much synonymous with “abstraction”. I think it’s better to reserve the word “indirection” to describe abstractions that require you to look elsewhere to understand what the abstraction actually is.

Re: Avoid Indirection in Code

#38

Okay, maybe it's just a poor example, but in the example used, the problem is definitely not too much indirection. If I was doing a code review and came to this: def is_foolike(x): return x.startswith("foo") I would comment, but my comment would be, "Could you name this function `starts_with_foo`?" This addresses both concerns mentioned in the article: 1. During review, when a reviewer is asked to verify that code is…

`is_foolike` is a bad name if you name your methods to only describe how they are implemented internally. At a high-level, in your business domain, you generally write your code to hide the implementation details and to describe the intention of the method EG. `is_carbonated` if is_carbonated(beverage): beverage.jiggle(false) vs if beverage.startswith("co2"): beverage.jiggle(false) (edit) for formatting

I'm sorry that this will come across as rude, but I feel like you're repeating something you've heard but didn't quite understand.

Yes, at pretty much ANY level, you should write your function names to hide the implementation details and to describe the intention of the function. In my previous post I hinted that the name of the function should "describe what the function does", which is the same as "describe the intention of the function", because if the function doesn't do what it's intended to do, that's a bug.

The reason I say I don't think you understand what you're saying is that none of what you're actually saying applies to the examples you're giving.

`is_foolike` doesn't describe the intention of the method. It gives an entirely vague and inaccurate view of what the method does. So even though we both agree that functions should describe the intention of the method, you're saying `is_foolike` is an okay name even though it doesn't do what you say it should?

`starts_with_foo` describes the intention of the method. It doesn't describe the implementation: `starts_with_foo(x)` might expand to `x.startswith('foo')` or `x[:3] == 'foo'`, but I don't care which, because the name accurately describes what it does either way.

Your example doesn't elucidate. If we're representing beverages as strings which are somehow guaranteed to begin with "co2" if the beverage is carbonated, and we've decided that the deserialization should be mixed into our "high-level, in your business domain", the program is so badly tangled that we're not going to get any truths about good programming from it.

Re: Avoid Indirection in Code

#39
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!

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.

Re: Avoid Indirection in Code

#40
It's not indirection, it's bad abstraction that's the real issue here. Consider the example used in the article:

    if x.startswith("foo"):
      do_something_with(x)

    if is_foolike(x):
      do_something_with(x)
The problem with both of these variations is that the "if-statement" doesn't have any meaning behind it. There's no gain in the indirection presented here. Whereas the following code has meaning:

    if checkHasPermissions(x):
      do_something_with(x)
The reader of the function is now able to understand the purpose of the indirection and the function has clear responsibilities.

Remember although code is interpreted by machines it's read by humans...

Post reply on HN