Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Abstractions lead to coupling and complexity.

The wrong abstractions can lead to coupling and complexity. The right ones on the other hand are all about reducing coupling and complexity.



> The wrong abstractions can lead to coupling and complexity.

This comes to the old quote:

* "Make everything as simple as possible, but not simpler.” Albert Einstein.

Abstractions undoubtedly add complexity that quickly becomes unmanageable. By now everyone is already aware of the horror that's the enterprise version of Hello World, and YAGNI/gold plating are renowned antipatterns. Many codebases have already succumbed to the perils of premature generalization, where the good old rule of 3 of refactoring serves as a shield against it.

But still some developers succumb to the siren song of abstracting away things.


Okay, let's take an excerpt from one of those enterprise "hello world"s:

    IHelloWorldString helloWorldString = helloWorld.getHelloWorld();
    IPrintStrategy printStrategy = helloWorld.getPrintStrategy();
    IStatusCode code = helloWorld.print(printStrategy, helloWorldString);
Extracting two subobjects from an object to feed them back to the same very object is not an abstraction, it's merely adding a bunch of public methods (and interfaces) to the object. That may or may not help in abstracting things: and usually, the more handles and bells and whistles are available to pull and play with, the less abstracted the code actually is.


There's using "abstraction" in the classical sense of "harder to understand", as in "abstract painting". In today's world, good abstractions are the exception.

> it's merely adding a bunch of public methods (and interfaces) to the object

The object is the abstraction they're complaining about. Why have an object to begin with?


> it's merely adding a bunch of public methods (and interfaces)

What do the interfaces represent?


In this case, nothing particularly meaningful or useful, they mostly just shrink wrap the underlying (single) implementation's details and re-expose them as-is for the caller to cope with. Some people also think that that somehow helps with encapsulation too.


> In this case, nothing particularly meaningful or useful

You're either playing dumb or weren't able to understand what was in the code. IHelloWorldString is an abstraction over the way the string was implemented, IPrintStrategy is a strategy pattern that abstracts away how the abstract hello world string is supposed to be printed, and finally IStatusCode is an abstraction over how a status code is implemented.

> they mostly just shrink wrap the underlying (single) implementation's details and re-expose them as-is for the caller to cope with.

No, not really. Their purpose is to abstract away implementations. Just because there's a single implementation that does not mean this wasn't abstracted away.


How is gold plating the same as YAGNI? They're opposites, no?


> How is gold plating the same as YAGNI? They're opposites, no?

No, gold plating and YAGNI are two faces of the same abstraction coin.


Under-engineering is not the same thing as YAGNI.

I worked at a place where the former employee had engineered his whole S3 to files synchroniztion layer. We never needed that. That's YAGNI. (And if we did, the library already has it!)

Choosing crappy overly-simple ideas for abstraction is not what YAGNI is all about. It's about pulling in overhead+complexity when it's justified, and only then.


We have a history of abstraction-based code that’s turned applications into frozen balls of mud.

We have so many tools to help write good code that the short-term savings of shared libraries is superseded by having distinct codebases that can be modified without those dependency concerns. Same is true for finding bugs in many places and easily setting those up for maintenance releases.


There is no going around complexity. We are absolutely standing on the shoulders of huge giants; even if you think you don’t have any dependency, behind the scenes you use many decades-old libraries dealing with the complexity of floating point arithmetics and such.

Also, Brooks paper is still true, the only real, significant productivity boost is reusing existing code. Even if you rewrite everything from scratch, you will still have to carry the exact same amount of essential complexity. Managing complexity is pretty much the most important part of CS.


open any network communications protocol RFC or any CPU design architecture or operating systems memory tables or operating systems filesystems with journaling or hardware I/O

it is crazy that society hasn't collapsed yet


Some engineer from Netflix once said something along the lines: "Microservices, not too many, mostly alongside team boundaries". Meaning most microservices should be wholly owned by a team and a team should ideally own only one microservice (but exceptions are allowed under special circumstances)

Like microservices, I feel code abstractions should be thought in a similar way, mostly a thing we use in our team, not company wide

In my own team I have to fight very hard to keep dependencies to external code down, the amount of entangling people are willing to put their code through is just crazy. An argument I often put is: "Why should we use lib X from team Y from my company if they don't even feel it is good enough to open source?". Any external code that my team imports into our project should be good enough it could be open sourced (given IP-rights allowing)


I agree, besides, there's really no getting away from abstractions to manage complexity.


A stronger claim: everything in programming is an abstraction.

Somewhat tangential, but I sense tacitly related... I sometimes sense that many people who program adhere to a kind of computational atomism, that there is some kind of underlying "real" and that everything else is "just" some kind of arrangement of elements of the real. But that's just an implementation detail. Yes, when we implement a language that compiles to instructions or bits on a specific machine, we are indeed working to simulate that language. But computation and formal languages don't really have anything to do with physical computers. Their connection is entirely incidental. It is a matter of practicality, like the choice of using a hammer versus a rock to drive spikes of metal through wood. A computer language is the "base" language. From the formal perspective, there is no "low-level" or "high-level" language, just different languages that compilers can translate between. What is "low-level" (typically the target language) is merely low-level by convention, usually because we are targeting a given machine instruction set of some physical machine.

But even here, the notion of an "instruction" or a "bit" are abstractions. There are no "bits" in the world as ontological entities. Computation and data are abstract, full stop. All physical implementations are simply instruments for simulating that abstract model. There is no difference, in principle, between using checker pieces, differences in voltage, magnetic polarity, or thumbs up/thumbs down to represent bits.

The language should, ideally, be suited to the domain of discourse.


Arguably managing complexity is one of the main things you do when doing programming


Over time all abstractions become “wrong” as requirements change.


You shouldn't be baking business logic into your abstractions like that.

Or, perhaps this is a better way of putting it: if there's business logic mixed into it, it's not an abstraction, it's a concretion.


This is why refactoring is a thing.

Perhaps someone should write a book for managers that explains these two things.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: