Live data from Hacker News

Premature Abstraction

arendjr.nl

91–100 of 116 posts

Re: Premature Abstraction

#91

I would love a language that has this gradual evolutional abstracting as a core concern. That makes it easy. Where you can start from simplest imperative code and easily abstract it as the need for this arises. For example a language that requires "this." or "self." prefix is not such language because you can't easily turn a script or a function into a method of some object.

> I would love a language that has this gradual evolutional abstracting as a core concern. That makes it easy. Where you can start from simplest imperative code and easily abstract it as the need for this arises.

This is about how I write Clojure.

I start out with some code that does the thing I want. Either effectfull code that "does the thing" or functions from data to data.

After a while, I feel like I'm missing a domain operation or two. At that point I've got an idea about what kind of abstraction I'm missing.

Rafael Dittwald describes the process of looking for domain operations and domain entities nicely here:

https://youtu.be/vK1DazRK_a0

Re: Premature Abstraction

#92
post #15

Earlier quoted context omitted.

This is a strong argument against inheritance, but that isn't everything about OOP. Just one well supported advanced abstraction (That I would also argue should be rarely used.) I would argue that just having strong type system and bundling methods with data gets you the vast majority of the usefulness of OOP. Liskov, Open/Closed, Message Passing, and other theoretical abstractions be damned. EDIT - Where are the goo…

> I would argue that just having strong type system and bundling methods with data gets you the vast majority of the usefulness of OOP. Yes, a module system brings almost all of the advantages of OOP. The one remaining is structure abstraction (things like interfaces on Java derived languages, or type classes on Haskell derived ones). But well, none of those are even typically associated with OOP. The OOP languages j…

Yep. Rust has all of these features (modules, structs with associated methods and type classes (traits)). But nobody thinks of it as an OO language. In fact, I’ve heard that many people struggle with rust if they’ve come from a heavily OO language like Java. You have to structure your code a little differently if you don’t have classes.

Modula apparently had many of these features too - and that predated what we now think of as object oriented programming. The good parts of OOP aren’t OOP.

Re: Premature Abstraction

#94
post #26

Earlier quoted context omitted.

> The real world definitely isn't Ironically, (ime/o!h;) it turns out this the "root of evil" plaguing abstraction : thinking that software architecture must map to "the real world".

I think you have it backwards. Software exists in the real world, and is used to solve real world problem. In building software we inevitably invent or use abstractions to represent or effect real world things. Abstractions that make it easier to do this are good, abstractions that make it harder to do this are just getting in the way.

> In building software we inevitably invent or use abstractions to represent or effect real world things.

Ehhh. Most abstractions I’ve written aren’t abstractions over the real world. They’re abstractions over low level machinery of a computer or program. (Eg there’s no HTTP request and response, DB connection or network socket outside of computer software).

The real world isn’t object oriented. It’s just a bunch of atoms moving around. You can describe physical reality as a bunch of objects that interact via ownership and method calls, but there’s nothing natural about that. OO is no better of a way to describe the real world than actors & message passing, or state & events.

Software that models “the real world” usually describes users, money, addresses and things like that. But none of those things are made out of atoms. There is no money molecule. Money is not on the periodic table. They’re all just another form of abstraction - one that happens to exist outside the software world, that we can capture in part in a database table.

Interesting abstractions are all invented ideas. Some are useful. Some are elegant to express and use in OO code, and some are not.

Re: Premature Abstraction

#96

This seems more like premature indirection than abstraction. There are other design “paradigms,” such as denotational design . You start with and build from abstractions.

I used to read and write a lot of Scala and a bit of Haskell code which claims to be very similar to what I think you're mentioning here. There people also start with defining the domain in interfaces (algebras, eDSLs) and data types.

In the end it's still the same indirection and abstraction as in any other Java or Go codebase, and it prevents the developer from easily accessing the actual logic of the program.

Re: Premature Abstraction

#97
Premature Abstraction is a common problem, affecting developers of all ages. Most are too embarrassed to talk about it.

Fortunately, there are non-invasive therapies that can reduce the frequency of occurrence.

Re: Premature Abstraction

#98
post #21

Earlier quoted context omitted.

> dynamic dispatch makes it harder for newcomers to figure out the business logic by reading the code This is definitely a potential problem, but I note that you can also get into this mess without OO in any language that lets you put a (reference to) function in a variable. Or, god help you, operator overloading.

Operator overloading isn't to blame. If I overload shift-left ` That said, unless you are writing a math library or some container, there's not many good uses for operator overloading.

I think the main difference between the two is, as someone reading and debugging the code, will probably eventually check even those methods that I assume I know roughly what they do. In contrast, I may not even think to check an overloaded operator unless I _already know_ that it's overloaded.

Maybe a good analogous method would be an overloaded `.ToString()` in C# that has side effects or returns the full text of the Magna Carta or something.

Re: Premature Abstraction

#99
I've been a software eng professionally for 25 years. Have been coding more like 30 - 35. There is a fundamental principal here that I agree with and it surrounds a code smell that Martin Fowler termed "Speculative Generality" in his book "Refactoring."

Speculative Generality is when you don't know what will have to change in the future and so you abstract literally everything and make as many things "generic" as you possibly can in the chance that one of those generic abstractions may prove useful. The result is a confusing mess of unnecessary abstractions that adds complexity.

However, yet again I find myself staring at a reactionary post. If developers get themselves into trouble through speculative generality, then the answer is clearly "Primitive Obsession" (another code smell identified in "Refactoring") right?

Primitive Obsession is the polar opposite of abstraction. It dispenses with the introduction of high-level APIs that make working with code intuitive, and instead insists on working with native primitive types directly. Primitive Obsession often comes from a well meaning initiative to not "abstract prematurely." Why create a "Money" class when you can just store your currency figure in an integer? Why create a "PersonName" class when you can just pass strings around? If you're working in a language that supports classes and functions, why create a class to group common logical operations around a single data structure when you can instead introduce functions even if they take more parameters and could potentially lead to other problems such as "Shotgun Surgery."

This is not to say that the author is wrong or that one should embrace "premature abstraction." Only that I see a lot of reactionary thinking in software engineering. Most paradigms that we have today were developed in order to solve a very real problem around complexity at the time. Without understanding what that complexity was, historically, you are doomed to repeat the mistakes that the thinkers at the time were trying to address.

And of course, those iterations introduced new problems. Premature Abstraction IS a "foot gun." What software engineers need to remember is that the point of Design Patterns, the point of Abstractions, the point of High-Level languages and API design is to SIMPLIFY.

One term we hear a lot, that I have been on the war path against for the past decade or two is "over engineering." As engineers, part of our jobs is to find the simplest solution to a given problem. If, in your inappropriate use of a given design pattern or abstraction, you end up making something unnecessarily complicated, you did not "over engineer" it. You engaged in BAD engineering.

When it comes to abstractions, like anything else, the key to gain the experience needed to understand a) why abstractions are useful b) when abstractions can introduce complexity and then apply that to a prediction of what will likely benefit from abstraction because it is something that will be very difficult to change later.

All software changes. That's the nature of software and why software exists in the first place. Change is the strength of software but also a source of complexity. The challenge of writing code comes from change management. Being able to identify which areas of your code are going to be very difficult to change later, and to find strategies for facilitating that change.

Premature Abstraction throws abstractions at everything, even things that are unlikely to change, without the recognition that doing so makes the code more complex not less. Primitive Obsession says "we can always abstract this later if we need to" when in some situations, that will prove impossible(ex: integrating with and coupling to a 3rd party vendor; a form of "vendor lock-in" through code that is often seen).

/stream-of-consciousness-thoughts-on-article

Re: Premature Abstraction

#100
post #75

Earlier quoted context omitted.

Yes. Did I mention there are interfaces too, with almost the same name, but it’s only used for the base classes. (Yes, there is only one implementation of all interfaces).

Having lots of interfaces for common things is not a bad thing. See how Rust traits work... even basic structs you create will probably implement lots of basic traits (some of which can be done automatically, thankfully) like `Display`, `Default`, several `From` or `Into` impls, `Clone`, `Copy` if your type is "light", `AsRef`, `Send` and many more! This makes code much more reusable as so many functions are written…

The issue isn't the interfaces; it's that there is only one implementation (the base classes) per interface—so why even bother having an interface? Otherwise, I agree with you to a degree.

The main issue with the codebase is that if you want to, say, change the behavior of a Book, you have to go change the behavior of some base class (after working out which one is actually being called). This base class might be used in a Clock, Field, or Filesystem as well—something so conceivably far away that their similar behavior is a coincidence and not really related at all. Then you get to argue with the architect about whether "reading a book" is different than "reading a clock."

Post reply on HN