Hacker News new | ask | show | jobs
by MauranKilom 2498 days ago
Out of all the comments here this one resonated with me by far the most.

You're totally correct: The goal is to find the right abstractions. Bad abstractions keep getting in your way either by leaking too much or by being based on invariants that turn out to be not actually be all that invariant, whereas good abstractions have no need to be touched again later.

For me, the additional layer of "how hard is it to change the abstraction if it turns out to be wrong" is also quite important. In my environment (algorithmic R&D) you never quite know which invariant your next idea will break. And this is where OOP can really bite you - untangling code from a class hierarchy or twisting interfaces to bend to your new ideas is anywhere between infeasible and a complete mess. Composition over inheritance can help save a lot of frustration here. But sometimes interface/abstract base class + a single implementation also provides a very clean abstraction to separate the core model/algorithm from the fiddly details. Just don't fall for deep hierarchies or large interfaces would be my takeaway so far.

Maybe we just need to get better at giving up on abstractions when they fail? I think that's very tough because they tend to still dictate how we think about problems (plus the usual cost of rewriting code). Your approach of delaying the abstraction choice sounds very interesting (somewhat akin to Extreme Programming?), I will try to keep that in mind for the future. In thinking about this I'm mostly afraid of code sort of remaining at this "makeshift"/provisional level and never actually settling into something more organized (due to lack of incentive/time to improve it). I think that can be kept under control, but do you have any suggestions on that front?

1 comments

> Your approach of delaying the abstraction choice sounds very interesting (somewhat akin to Extreme Programming?), I will try to keep that in mind for the future. In thinking about this I'm mostly afraid of code sort of remaining at this "makeshift"/provisional level and never actually settling into something more organized (due to lack of incentive/time to improve it). I think that can be kept under control, but do you have any suggestions on that front?

Actually, I find that more often than not, code written in this manner is actually easier to maintain, because there are no layers and concepts to untangle - just the raw functionality and data transformations. You don't need to put yourself in the mindset of the person who wrote the code, who might not have had the right concepts in their head at the time. Instead, you just parse the code like a computer - This makes it easier to figure out what the code actually does rather than being distracted by the intent of the author.

The main purpose of abstraction is to intentionally hide implementation details so other people can use your code without requiring a full understanding of all the details. However, when maintaining code, obfuscation is the exact opposite of what you want - You want to fully understand what the code is actually doing. Internally, I forgo most abstractions. For public interfaces, I expose highly specific functions and datatypes rather than "generic" interfaces, and only the bare minimum. Variants should favor duplication over generalization when uncertain. It's up to the consumer of those APIs (which is often myself!) to choose whether or not to implement abstractions on top of those. In my experience, it's either super-obvious when it's needed or unnecessary if unsure - So you can usually push back abstraction up to that point.