[HN Gopher] That's not an abstraction, that's a layer of indirec...
       ___________________________________________________________________
        
       That's not an abstraction, that's a layer of indirection
        
       Author : fagnerbrack
       Score  : 509 points
       Date   : 2024-12-25 05:06 UTC (3 days ago)
        
 (HTM) web link (fhur.me)
 (TXT) w3m dump (fhur.me)
        
       | danparsonson wrote:
       | Perhaps this is a minor nitpick, but
       | 
       | > Abstractions are also the enemy of simplicity. Each new
       | abstraction is supposed to make things simpler--that's the
       | promise, right?
       | 
       | Not exactly, no. The purpose of abstraction is to hide
       | implementation detail, and thereby insulate one part of the
       | codebase/application/system from variations in another. Graphics
       | APIs for example - yes your code may be simpler for not having to
       | deal with the register-level minutiae of pushing individual
       | triangles, but the core benefit is that the same code should work
       | on multiple different hardware devices.
       | 
       | Good abstractions break a codebase up into compartments - if you
       | drop a grenade in one (change the requirements for example), then
       | the others are unaffected and the remedial work required is much
       | less.
        
         | uoaei wrote:
         | In short: good abstractions simplify by centralizing
         | operational logic. But it's not until a certain scale where
         | that option is more efficient from bespoke implementations.
        
           | magicalhippo wrote:
           | A heuristic we use at work is to not introduce an abstraction
           | layer until there are at least two different implementations
           | required.
           | 
           | That is if you think you'll probably need multiple
           | implications, delay introducing an abstraction until you
           | actually do.
           | 
           | Also, there are different ways of providing abstraction.
           | 
           | Perhaps you don't need to abstract the entire implementation
           | but, as an example, rather change one parameter from passing
           | a value to passing a function returning a value.
        
             | hansvm wrote:
             | That touches on a couple related principles:
             | 
             | - not doing extra work now if it's not necessary yet and if
             | it's as cheap to do later
             | 
             | - delaying building a thing till you know what that thing
             | should be
        
         | wvenable wrote:
         | A good abstraction is like documentation written in code.
        
         | bruce511 wrote:
         | >> but the core benefit is that the same code should work on
         | multiple different hardware devices.
         | 
         | I came here to say this. Attractions act as a bridge between
         | things which allow those things to change independently.
         | 
         | Using an ORM allows my program to easily work against multiple
         | sql databases. Using a compiler allows me to target different
         | hardware. Using standard protocols allows me to communicate
         | with different programs. Using libraries to do say email hides
         | me from those protocols and allows me to adapt to service
         | providers Using APIs not protocols.
         | 
         | In other words the abstraction is designed to hide a layer
         | (which can change) from a program not interested in that level
         | of change.
         | 
         | The key is to stop abstracting when the program cares. By all
         | means encapsulate rules and processes, but they're a direct
         | implementation of those rules and processes.
         | 
         | One can argue my "calculateLeaveForEmployee" function is an
         | "abstraction" - but that would be a misnomer. Since there's
         | only one set of rules in play (the set that matters now) its an
         | implementation. An abstraction supports (at least) two things
         | at the same time.
        
           | necovek wrote:
           | > Using an ORM allows my program to easily work against
           | multiple sql databases.
           | 
           | A curious example, since most developers who've worked on any
           | project with significant amount of data in a database would
           | likely disagree.
           | 
           | IMO, ORMs mostly allow using programming-language-of-choice
           | as a syntax for relational queries instead of constructing it
           | by hand on top of serialization and deserialization of
           | objects into rows and vice versa.
        
             | ludston wrote:
             | Indeed, given that most sql dialects have subtle
             | differences that make them noncompatible with one another,
             | and most ORMs have support for dipping into raw sql,
             | sufficiently large systems tend to end up coupled with a
             | particular database anyway. That's not to mention that the
             | BAs will want raw sql access for report writing and
             | switching systems breaks all of their scripts too.
        
             | zharknado wrote:
             | Yes, ORMs came to mind for me as an example of indirection
             | without abstraction. If you accept OP's litmus test of "how
             | often do I have to peek under the hood" I think ORMs
             | generally don't score particularly well.
        
               | 9rx wrote:
               | If enabling the ablity to think of the database as the
               | rich native data structures provided by the host
               | programming language, and not the tables (rows and
               | columns) that the underlying database service wants to
               | think of the data as is not an abstraction, what is?
        
               | zharknado wrote:
               | Fair, it's probably incorrect to say there's "no
               | abstraction" in an ORM, maybe more precise to say it's
               | quite a leaky one. I've never seen an ORM used in the
               | wild where you could actually avoid thinking about tables
               | and columns and rows pretty frequently. But that's
               | admittedly armchair anecdotal.
               | 
               | In the small-to-medium scale cases I've seen, the main
               | re-use you get for a particular mapping is when an object
               | is used as a child node elsewhere. If it's a one-off
               | you're essentially writing a bespoke shorthand wrapper
               | for some custom SQL. Which may be a convenient shortcut
               | or prettifier, but isn't really functioning as an
               | abstraction. Net net it seems like more overhead. Or like
               | the real benefit is constraining database design to
               | closely align with object design (which could be valuable
               | but isn't a function of abstraction).
               | 
               | This is all assuming code ownership across the stack.
               | With a separate team managing data representation and
               | handing reliable object contracts "over the wall" I could
               | imagine a situation where the consumer gets to think
               | purely in objects. I just haven't observed that in
               | practice. E.g. What if the contact has two address
               | records? What if the number of purchase records exceeds
               | the maximum result size? If things like that come up
               | regularly enough, it's worse when you're waiting on a
               | separate team to fix the leaks.
        
               | 9rx wrote:
               | _> I've never seen an ORM used in the wild where you
               | could actually avoid thinking about tables and columns
               | and rows pretty frequently._
               | 
               | There is a compelling argument that absent the n+1
               | problem, it could be a leak-free abstraction. And, in
               | practice, SQLite doesn't suffer from the n+1 problem...
               | But that is definitely true in a lot of cases,
               | particularly where the n+1 problem is unavoidable.
               | 
               |  _> If it's a one-off you're essentially writing a
               | bespoke shorthand wrapper for some custom SQL._
               | 
               | While the original comment also conflated query building
               | and ORM, I am not sure that we should. The intersection
               | of query building and ORM was historically referred to as
               | the active record pattern. I suspect the ActiveRecord
               | library, from Rails, calling itself an ORM is where
               | things started to become confused. Regardless, whatever
               | you want to call the data mapping and query building,
               | they are undeniably distinct operations.
               | 
               | It may be fair to say that query builders are no more
               | than an indirection. That said, I posit that they only
               | exist as an abstraction to model composability on top of
               | SQL. If SQL naturally composed through basic string
               | concatenation, I expect the use of query builders would
               | quickly disappear.
        
               | necovek wrote:
               | But SQL is "naturally composable" using string
               | concatenation. I've seen one too many of
               | tables = ["order", "product"]       join_on =
               | ["order.product_id = product.id"]       filters = []
               | 
               | as the base of the query, with all of these being
               | concatenated together.
               | 
               | But instead of this, an ORM usually provides you with a
               | syntax (that will pass syntax checks and have
               | highlighting) that matches the language, which is all
               | nice and good because dealing with arbitrary strings does
               | suck.
               | 
               | I've seen ORMs being used for query composition way
               | before Rails even existed: I believe Hibernate had HQL
               | with their first release in 2001, just like SQLObject did
               | in 2002. I am sure neither of those "invented" the
               | pattern.
               | 
               | Note that fetching objects using an ORM library,
               | filtering and such is what I also consider query
               | composition (it just happens behind the scenes).
        
               | necovek wrote:
               | ORM, as the name suggests, is an "adaptation" layer
               | ("mapping" or "mapper").
               | 
               | It does not really abstract _away_ relational data, it
               | instead _maps_ it to object /class hierarchy. And every
               | single ORM I've used also provides an interface to map
               | function/method/expression syntax to SQL queries to get
               | back those rows (adapted into objects).
               | 
               | Now, in a very broad sense, mapping would also be an
               | abstraction -- just like an "indirection" would be -- but
               | considering the narrower definition from the OP (where
               | indirection is not an abstraction), I think it's fair to
               | say that ORM is also not an abstraction.
        
             | mcfedr wrote:
             | It's interesting that some modern ORMs are dropping trying
             | to abstract out the dB engine. Specifically thinking of
             | Drizzle for js. And rather just focusing on the programming
             | interface
        
           | javcasas wrote:
           | > allows my program to easily work against multiple sql
           | databases
           | 
           | How many times have you required that your program runs
           | against different sql databases without modification?
           | 
           | I mean, how many times have you required your plane to be
           | able to fly in the different atmospheres of different planets
           | of the Solar System?
           | 
           | Unless you are NASA, I suggest you cut the complexity and
           | just make a plane that can fly well on Earth's atmosphere.
        
             | bruce511 wrote:
             | >> How many times have you required that your program runs
             | against different sql databases without modification?
             | 
             | Our main commercial product currently supports 2 database
             | engines, and we'll be offering a 3rd next year. For
             | enterprise offerings it's pretty common for the client to
             | prefer, or outright specify, the engine.
             | 
             | Commodotizing your complementary technology is a good way
             | to not become dependent on any specific database, and hence
             | can pivot quickly when required.
             | 
             | Fortunately I don't have a plane, so I don't have code to
             | fly in any atmosphere.
        
               | javcasas wrote:
               | Yes, yes, you are NASA.
               | 
               | The rest of us go with Postgres, or SQL server, or
               | Oracle, but definitely don't have to prepare our systems
               | to run in PostgreSQL on Mondays, on SQL server on
               | Tuesdays and so on.
        
               | Mawr wrote:
               | If the pivoting never happens then the effort will have
               | been wasted. Adding features because of "what if" is the
               | worst engineering sin.
        
         | cpeterso wrote:
         | "The purpose of abstraction is not to be vague, but to create a
         | new semantic level in which one can be absolutely precise." --
         | Edsger Dijkstra
         | 
         | But sometimes a new semantic level isn't needed. Abstraction
         | gets so much press when you might just need some good ol'
         | fashioned information hiding and separation of concerns.
        
           | crabmusket wrote:
           | This is such a great quote, and helps explain what is a good
           | abstraction.
           | 
           | Because CRDTs have been in the zeitgeist a lot lately, I want
           | to pick them as an example of a "good" abstraction.
           | 
           | CRDTs have mathematical properties which can be described and
           | understood independently of a specific implementation. And
           | importantly, you can judge whether an implementation is
           | correct with reference to these abstract rules.
           | 
           | This means that when using a CRDT, you largely can treat it
           | as a reliably-solved problem. Once you understand the
           | concepts, and work out how to use the library you've picked,
           | you don't have to think about the details. Though that
           | doesn't mean sometimes the behaviour can be surprising:
           | 
           | https://www.moment.dev/blog/lies-i-was-told-pt-1
           | 
           | TCP and HTTP are great examples too, though interestingly I
           | don't know if they rely on mathematical definitions so much
           | as just being extremely widespread to the point that reliable
           | implementations are available anywhere you care to write
           | code.
           | 
           | I like this article which also leans on the Dijkstra quote:
           | 
           | https://www.pathsensitive.com/2022/03/abstraction-not-
           | what-y...
        
             | sgarland wrote:
             | CRDTs are also an excellent example because of how their
             | supporting infrastructure is impacted by their design,
             | namely that Postgres' method of dealing with updates makes
             | for massive write amplification.
             | 
             | I wrote this [0] previously, but it still applies. IMO, as
             | a dev, there are times where you really do need to think
             | somewhat deeply about infrastructure choices.
             | Unfortunately, knowing when you need to care practically
             | requires you to already understand it.
             | 
             | [0]: https://news.ycombinator.com/item?id=40834759
        
           | chii wrote:
           | > some good ol' fashioned information hiding and separation
           | of concerns.
           | 
           | that's exactly what you do to implement an abstraction isnt
           | it?
        
             | masklinn wrote:
             | In the same way wood is used to build a house.
             | 
             | That you used wood doesn't mean you built a house.
        
               | agentultra wrote:
               | Abstractions require theorems/laws/properties/rules...
               | whatever you want to call them.
               | 
               | Indirection is about providing a common language, and
               | interface if you will, for talking to different
               | implementations.
               | 
               | Separating concerns is another strategy altogether for
               | managing resources.
        
           | swed420 wrote:
           | It seems like almost everybody in these comments as well as
           | OP are failing to differentiate between the narrow computer
           | science / Dijkstra definition of abstraction versus how it's
           | invoked colloquially, including in conversations of computer
           | science such as here.
           | 
           | In that sense, we can correctly say that even encapsulation
           | etc is a form of (colloquial) abstraction, but not Dijkstra's
           | use of the word abstraction.
        
           | marcosdumay wrote:
           | Yeah... That's also an unachievable ideal.
           | 
           | On practice, there exist platonic abstractions that hide
           | mechanisms and can be this way, but you also need to abstract
           | parts of your requirements. And you will never be able to
           | achieve that while abstracting your requirements.
           | 
           | Dijkstra was an academic after all, and academics usually
           | don't care about complex requirements.
        
         | voidhorse wrote:
         | While this is how the term is often used, I think it's cavalier
         | use of language and confuses abstraction for _modularity_ , and
         | this linguistic confusion is one of the reasons a lot of
         | programmers write bad "abstractions".
         | 
         | Organizing your code into components that are as independent as
         | possible is a good practice and is the pursuit of modularity. A
         | proper abstraction on the other hand, is a generalization that
         | simplifies a _conceptual layer_ in your code base.
         | 
         | Abstraction often _enables_ greater modularity as a
         | consequence, but they are not the same thing. For example, in
         | the problem of text editing, people eventually realized that
         | the manipulation of text is typically line oriented. Thinking
         | of a text file as a collection of lines may seem like an
         | obvious and modest abstraction, but it works well. This
         | abstraction, in turn, leads to other couplings (e.g. line
         | oriented motion is highly dependent on the line abstraction),
         | but it also leads to potential modularity (e.g. printer code
         | may no longer need to understand exactly how a display renders
         | each character of text in a grid, instead, it too can work on
         | "lines"). Good abstractions support modularity to the extent
         | that they establish a shared domain of objects to communicate
         | about and across systems, but they do not necessarily produce
         | modularity in themselves.
        
           | danparsonson wrote:
           | Yes, abstraction and modularity are coincident but distinct
           | topics, and my last paragraph implies that I am equating
           | those two things. Good point.
           | 
           | I disagree that considering a text file as a set of lines
           | really qualifies as an abstraction; it's just a different
           | representation of the data - you could instead choose to use
           | a list of words or a tree or whatever. An abstraction is a
           | general interface to a concrete thing that allows you to
           | substitute different but similar concrete things without
           | changing the consumer of those things. The Linux filesystem
           | is a great example - I can 'cat' basically anything that has
           | a path, and some driver will pull data from the associated
           | endpoint and display the data on screen. "File" is the
           | abstraction, and consumers of files need not care about the
           | specifics of communicating with the underlying devices. Such
           | a system is also modular, but it's hard not to be when your
           | abstraction is well conceived.
           | 
           | Perhaps we're saying the same thing with different words?
        
             | mannykannot wrote:
             | When you write "an abstraction is a general interface to a
             | concrete thing that allows you to substitute different but
             | similar concrete things without changing the consumer of
             | those things", I feel you are actually describing a
             | _module_ created by _making use_ of abstractions.
             | 
             | Abstractions almost always represent data differently than
             | as it is in its most concrete form (e.g. lists and trees
             | are abstractions both often built of vectors and pointers),
             | but that is not the _purpose_ of abstraction. To continue
             | with the original example, the representation of text as a
             | sequence of lines is a sort of abstraction, but rarely the
             | most useful one: words, sentences, paragraphs and /or
             | syntax trees are often what you want, and they are useful
             | because they give us a representation of the raw text that
             | is aligned with its purpose, not because we can implement
             | them in multiple ways.
             | 
             | Furthermore, while we want to do some information hiding
             | when implementing these abstractions (think particularly of
             | syntax trees), that is not the purpose of having these
             | abstractions - what we are hiding is the infrastructure to
             | make it work, not any information conveyed by the text
             | itself.
        
           | usrusr wrote:
           | I don't like the mental image of layers. It's not wrong, but
           | it's terribly inclusive of everything that is bad about bad
           | abstractions. Even the worst abstractions will often look
           | kind of nice from the layers angle.
           | 
           | I prefer the concept of orthogonality: you have an entire
           | plane where your domain requirements can move in, but the way
           | you make persistence reliable is completely orthogonal to
           | that plane. It should be implemented in a way that does not
           | change when, say, your customer account becomes a composite
           | of of multiple identities. And neither should the way you
           | organize UI components change, or your transportation tooling
           | change. That entire transportation layer stack, from ethernet
           | all the way up to your domain model generator of choice.
           | 
           | Did I say layers? Yeah, I did. You'll often see the
           | orthogonal aspects themselves consisting of layers. But
           | that's an implementation detail and those layers tend to be
           | more pragmatic than dogmatic, as in the way TCP goes out of
           | its way to avoid hiding any of the addressing of the
           | underlying IP. TCP absolutely is a layer, but it has zero
           | ambition to perhaps run on top of something other than IP, or
           | hide differences between IPv4 and IPv6 from higher layers. It
           | focuses on one thing, implementing streams on top of packets,
           | again a problem nicely orthogonal to the problem of routing
           | those packets (what IP does) or anything built on top of
           | those streams.
        
             | vacuity wrote:
             | To add on to your great comment, I've read a few papers
             | that justify your remarks, but some have paywalls and I'll
             | try to condense them anyways.
             | 
             | Parnas' criteria for when to split code into modules[0] is
             | very compelling: modules should enforce information hiding,
             | where the information of each module is how a key decision
             | is implemented. Key decisions should be orthogonal: e.g.,
             | encryption shouldn't inform filesystem access and vice
             | versa. This fits in nicely with practices for determining
             | solutions for a given problem: nail down key decisions (or
             | uncertainties around them) early on and encode them through
             | modules and interfaces.
             | 
             | A logical application of Parnas' module criteria is,
             | naturally, in network stacks. According to [1], although
             | coarse program flow generally follows the OSI layers, the
             | fine control flow may hop around quite a bit. For some time
             | people tried to keep the OSI layers as inspiration and use
             | hacks like upcalls to selectively violate layering, which
             | is a sure sign that the layers are unhelpful. Instead,
             | modules should reflect functionality like encryption, NIC
             | interfacing, and routing. Aim for a flatter, horizontal
             | architecture like a B-tree, not a contrived vertical tower
             | with hacks to cross layers "incorrectly". There will be
             | hierachies and layers, but they should be obviously
             | necessary now. Layering is not the end or even the means
             | but merely how things are.
             | 
             | A program's capability (functionality) is determined by the
             | capabilities of its dependencies, and splitting up
             | functionalities well is important. I use this language to
             | relate it to capability-based security[2]. After all,
             | "security" is basically just a stricter form of program
             | "correctness", and just as a terminal emulator's input text
             | handling capability shouldn't have much to do with its
             | window event handling capability because malicious input
             | shouldn't cause dangerous graphics bugs, there isn't much
             | logical reason to have overlap regardless. They govern
             | separate devices, protocols, and purposes. Capabilities
             | along the lines of capability-based security reify the
             | interfaces and provide strict enforcement.
             | 
             | Lastly, flexibility around protocols should be added or
             | discard as given by the precise problem at hand. Don't
             | preemptively choose some extreme, but instead be informed
             | by what is actually desired. It will yield substantial
             | implementation complexity and performance detriments[3] to
             | over-generalize, and of course over-specializing is just
             | inadequate.                 [0]
             | https://prl.khoury.northeastern.edu/img/p-tr-1971.pdf
             | [1] https://ieeexplore.ieee.org/document/662046       [2]
             | https://en.wikipedia.org/wiki/Capability-based_security
             | [3] https://ieeexplore.ieee.org/document/208098
        
         | zdragnar wrote:
         | > The purpose of abstraction is to hide implementation detail
         | 
         | Technically, that's encapsulation, though the sentiment is
         | close, I think.
         | 
         | I rather view it as a matter of semantics. At one low level,
         | you have operations that deal with some concrete interface or
         | API, etc. You bundle those operations up behind an abstraction,
         | providing methods whose names involve your application domain.
         | Perhaps they are still at a technical level, and you bundle
         | those up behind another abstraction, whose method names involve
         | your _business_ domain.
         | 
         | Yes, the lower level details are hidden from the higher levels,
         | but _the hiding is not the point_. The point is to be able to
         | write code that readily corresponds to the problem you are
         | trying to solve.
        
           | danparsonson wrote:
           | Hmmm I prefer Wikipedia's definition:
           | 
           | > In object-oriented programming languages, and other related
           | fields, encapsulation refers to one of two related but
           | distinct notions, and sometimes to the combination
           | thereof:[5][6]
           | 
           | > - A language mechanism for restricting direct access to
           | some of the object's components.[7][8]
           | 
           | > - A language construct that facilitates the bundling of
           | data with the methods (or other functions) operating on those
           | data
           | 
           | The hiding is absolutely the point of abstraction, in the
           | sense that caller can manipulate the subsystem without
           | knowing about (important) inner details of it. As I said,
           | graphics drivers are a great example - I just want to put a
           | triangle on the screen, I don't want to care about the
           | registers I have to set on an NVidia card vs those on an AMD
           | card, or what their memory maps look like, and I don't want
           | to have to rewrite my code when they release a new generation
           | of hardware. Drawing a triangle is an abstraction, hiding
           | away the details of how a specific graphics card achieves
           | that.
           | 
           | Think about what the word 'abstract' means - the idea of a
           | 'human' is an abstraction for bundles of molecules conforming
           | to a general pattern with broadly similar qualities but
           | almost infinite variability. The word conveniently hides a
           | wealth of detail that we usually don't need to think about;
           | if I tell you there are three humans in a room, you can make
           | use of that information without knowing anything about their
           | exact physical attributes or who they are.
           | 
           | I would refer to what you're describing as 'modelling' -
           | these are related topics but I don't see them as the same
           | thing.
        
           | voiceofunreason wrote:
           | Berard 1993 offers a good survey of the meanings of
           | Abstrasction, Encapsulation, and Information Hiding
           | 
           | https://web.archive.org/web/20071214085409/http://www.itmweb.
           | ..
        
         | mgaunard wrote:
         | That would be encapsulation, not abstraction.
        
           | danparsonson wrote:
           | I disagree; to save duplicating my other reply to a similar
           | comment: https://news.ycombinator.com/item?id=42529743
        
       | jongjong wrote:
       | This is a great point. Most modern software is riddled with
       | unnecessary complexity which adds mental load, forces you to
       | learn new concepts that are equally complex or more complex than
       | the logic which they claim to abstract away from.
       | 
       | I find myself saying this over and over again; if the abstraction
       | does not bring the code closer to the business domain; if it does
       | not make it easier for you to explain the code to a non-technical
       | person, then it's a poor abstraction.
       | 
       | Inventing technical constructs which simply shift the focus away
       | from other technical constructs adds no value at all. Usually
       | such reframing of logic only serves the person who wrote 'the
       | abstraction' to navigate their own biased mental models, it
       | doesn't simplify the logic from the perspective of anyone else.
        
       | mightyham wrote:
       | I forget which programming talk I watched which pointed this out,
       | but one extremely common example of this in Java is recreating
       | subsets of the Collections API. I've done this before, heck even
       | the Java standard library is guilty of this problem. When a class
       | has a full set of get/put/has/remove methods, it is often not
       | actually hiding the complexity of its component data structures.
        
         | oftenwrong wrote:
         | Rich Hickey on HttpServletRequest?
         | 
         | https://www.youtube.com/watch?v=aSEQfqNYNAc
        
           | mightyham wrote:
           | Yup that's the one. Thanks for linking it.
        
         | mrkeen wrote:
         | Good example of a bad abstraction. If you're speaking the
         | language (or "abstraction") of sets, you should see certain
         | terminology: union, intersection, disjunction. These words are
         | not part of the Java Set interface.
        
           | mightyham wrote:
           | I would actually argue that the Collections API itself is a
           | pretty good abstraction. It is often the case that
           | conceptually I just want to work with multiple things and
           | properties like order, duplicates, random access, etc. are
           | not particularly important (in fact, requiring them adds
           | inherent complexity). It's very useful that a vast amount of
           | the standard library data structures conform to this
           | interface or can create data views that conform to it.
        
       | getnormality wrote:
       | How did "abstraction" and "hiding complexity" become perceived as
       | such fundamental virtues in software development? There are
       | actual virtues in that ballpark - reusable, reliable, flexible -
       | but creating abstractions and hiding complexity does not
       | necessarily lead to these virtues. Abstraction sounds no more
       | virtuous to me than indirection.
        
         | lizzas wrote:
         | It can be good if done well. The goal is not abstraction but
         | understandability.
         | 
         | Any function call is abstraction after all. Unabstracted you
         | would just inline that code or use goto.
        
         | wvenable wrote:
         | The only way we accomplish anything is abstraction. It's the
         | basis of all computing.
        
           | MobiusHorizons wrote:
           | I disagree. There are base abstractions you can't avoid, of
           | course, like the machine code of your computer, or the
           | syscalls presented by it. Using these is not abstraction,
           | unless you choose to build up interfaces of reusable pieces.
           | Abstraction is structure. You still have to actually write
           | some code that can be organized into structure. You could
           | write code using just those base abstractions if you wanted,
           | or as many do, choose libc as your base abstraction. Watching
           | a program through strace gives you basically this view,
           | regardless of the abstractions the program actually used to
           | achieve the result.
        
             | wvenable wrote:
             | Some abstractions are so ingrained you don't even think of
             | them as abstractions. A file is an abstraction. A socket is
             | an abstraction. The modern terminal is an abstraction.
             | 
             | People only notice bad abstractions.
        
         | zahlman wrote:
         | Decades ago, when _The Structure and Interpretation of Computer
         | Programs_ taught us that programmers fundamentally do two
         | things: abstraction and combination; and we are interested in
         | programming languages insofar as they provide means to those
         | two ends.
         | 
         | The two classic "hard problems" of computer science - cache
         | invalidation and naming things - are both aspects of
         | abstraction. Cache invalidation is a special case of making
         | sure the abstraction does what it's supposed to, and naming is
         | the most important part of causing the abstraction to have
         | meaning.
        
           | wombatpm wrote:
           | I thought the two problems were cache invalidation, naming
           | things, and off by one errors
        
             | zahlman wrote:
             | Indeed. And by similar logic, abstraction is the _only_
             | thing we do. ;)
        
         | Starlevel004 wrote:
         | 90% of modern programming takes are shadowboxing Java 6
        
           | flocciput wrote:
           | Elaborate on that?
        
             | bluepizza wrote:
             | Not parent, but I have a similar impression. Design
             | patterns, clean code, and several of these well known tools
             | were particularly useful during C++ and early Java eras,
             | where footguns were abundant, and we had very little
             | discussion about them - the Internet was a much smaller
             | place back then. Most of the developer work was around
             | building and maintaining huge code bases, be it desktop or
             | server, monoliths were mostly the only game. And many
             | initiatives grew trying to tame the inherent hazard.
             | 
             | I think that microservices (or at least, smaller services)
             | and modern languages allow the code to stay more
             | manageable, to the point where Java devs now are able to
             | dismiss Spring and go for a much simpler Quarkus.
        
         | Barrin92 wrote:
         | >How did "abstraction" and "hiding complexity" become perceived
         | as such fundamental virtues in software development?
         | 
         | They aren't just fundamental virtues in software development,
         | they're the fundamental basis of all cognition. If I twiddled
         | with every bit in my computer I'd never write a hello world
         | program, if I wrestled with every atom in my coffee cup I'd
         | never drink a sip of coffee.
         | 
         | Abstraction and information hiding is the only way we ever
         | accomplish anything because the amount of information fitting
         | in our heads is astonishingly small compared to the systems we
         | build. Without systems of abstraction we would literally get
         | nothing meaningful done.
        
         | parpfish wrote:
         | Part of it has to be that finding and defining abstractions is
         | _fun_ like a puzzle so programmers like doing it and finding
         | ways to justify it after the fact
        
         | brookst wrote:
         | What languages do you work in? Would you be happier or more
         | productive if you had to be aware of the quirks of every ISA
         | and interrupt controller your code might run on?
         | 
         | Abstraction is good, in the way that leverage is good in the
         | physical world: it is not always necessary, but people who are
         | aware of the tool are vastly more capable than those who are
         | not.
        
       | feverzsj wrote:
       | What about java?
        
         | praptak wrote:
         | It's a huge ecosystem which turns 30 in May. Yes, it
         | accumulated lots stuff that was cool back then and isn't so
         | cool now but overall is doing pretty well (COBOL was 36 when
         | Java got public).
        
       | VirusNewbie wrote:
       | >There's a well-known saying: "All abstractions leak." It's true.
       | No matter how good the abstraction, eventually, you'll run into
       | situations where you need to understand the underlying
       | implementation details
       | 
       | This is false. One can read up on Theorem's for Free by Wadler to
       | see that not all abstractions are leaky.
        
         | stickfigure wrote:
         | This has such potential to be an interesting thread of
         | conversation but all I get is a reference to a book that I
         | haven't read and am unlikely to.
         | 
         | What examples of non-leaky abstractions do you have?
         | 
         | I could imagine something like "newtonian physics" but that
         | leaks into my daily life every time I fire up google maps and
         | get a GPS fix.
         | 
         | The OP's example of TCP seems close to the mark, but to be
         | totally honest I'm not convinced. Every time I have to run ping
         | to check whether my hung connection is due to connectivity, I'm
         | breaking the abstraction. And I've had to debug network issues
         | with Ethereal (yes, I'm dating myself). TCP does leak, it's
         | just that most people don't know what to do with it when it
         | does.
        
         | abstra4free wrote:
         | Theorems for Free tells you that some abstractions satisfy some
         | mathematical properties (for free!) under some circumstances.
         | 
         | If you write down a function with signature                 {T
         | : Type} -> T -> T
         | 
         | then it must be the identity function, if you do not use
         | "malicious" extensions of the type system.
         | 
         | But what is the performance of the identity function?
         | 
         | Here is an identity function:                 lambda T, lambda
         | t, if (2 + 2 = 4) then t else t
         | 
         | In other words: I can hide pretty much arbitrary computation in
         | my identity function.
         | 
         | Users of my identity functuon will notice that it is wicked
         | slow (in reality, I let my identity function compute Busy
         | Beaver 5, before doing nothing). Their complaints are evidence
         | of leaky abstraction.
         | 
         | Now you might have a smart optimizing compiler that knows about
         | Thm4Free... But that's another story.
        
           | javcasas wrote:
           | I don't think you can have such compiler, at least for a
           | general case, without solving the halting problem first.
           | After all, you can encode arbitrary computations at the type
           | level.
        
           | VirusNewbie wrote:
           | The point of the abstractions I'm talking about is not to
           | abstract away the _computation_ part of a program, it 's to
           | abstract away the types and complexities of the semantics.
        
         | lelanthran wrote:
         | "All abstractions are leaky, but some are useful" - me.
         | 
         | In fact, my own framework for small LoB webapps is based
         | entirely around this premise.
        
       | noduerme wrote:
       | I got a piece of advice writing UI code a long time ago: Don't
       | marry your display code to your business logic.
       | 
       | I'd like to say this has served me well. It's the reason I never
       | went for JSX or other frameworks that put logical code into
       | templates or things like that. That is one abstraction I found
       | unhelpful.
       | 
       | However, I've come around to not taking that advice as literally
       | as I used to. Looking back over 25 years of code, I can see a lot
       | of times I tried to abstract away display code in ways that made
       | it exceedingly difficult to detect why it only failed on certain
       | pieces of data. Sometimes this was sheep shaving tightly bound
       | code into generic routines, and sometimes it was planned that
       | way. This is another type of abstraction that adds cognitive
       | load: One where instead of writing wrappers for a specific use
       | case, you try to generalize everything you write to account for
       | all possible use cases in advance.
       | 
       | There's some sort of balance that has to be struck between these
       | two poles. The older I get, though, the more I suspect that
       | whatever balance I strike today I'll find unsatisfactory if I
       | have to revisit the code in ten years.
        
         | josh2600 wrote:
         | Ahh yes, the labor of love that is code maintenance.
         | 
         | Done is better than perfect until you're reviewing the code in
         | 10 years (or maybe 3 years with a more adept set of eyes over
         | your shoulders).
         | 
         | These days I ask my teams to be less clever and more simple.
         | 
         | Simple usually wins over clever in the long run.
        
           | mcfedr wrote:
           | Completely this. KISS, my favourite acro for this.
        
         | skydhash wrote:
         | I learned that lesson building an utility with JavaFX. I've
         | done a few years with React and the usual pattern was to move
         | almost everything out of the components. Unless it's an event
         | handler, a data transformer for the view, or something that
         | manipulates the view, it has no business belonging to this
         | layer.
         | 
         | I don't try to generalize it, I just try to make the separation
         | clear using functions/methods/classes. Instead of having the
         | post button's handler directly send the request, I create a
         | function inside the `api` module that does it. It does have the
         | effect on putting names on code patterns inside the project.
        
           | noduerme wrote:
           | When you say "components" do you mean that in the mixed React
           | sense where a component could contain HTML? In my own usual
           | cases, I call the Javascript a component, and the HTML a
           | template. I usually take a handlebars approach on the
           | template content and something like "data-role" to identify
           | the template tags to the JS, and beyond that don't mix them.
           | However, my client-facing JS components themselves are
           | totally bound to the templates they load up - they expect the
           | presence of certain fields to work with. I'm talking more
           | about not mixing any business logic into those JS components:
           | Let's say, in a form component, not anticipating that a
           | dropdown menu will have any particular shape or size of
           | dropdown item, which means those items need to be specified
           | separately. This leads to JS components relying on lots of
           | other components, when sometimes you just need one type of
           | dropdown item for a particular component, and having dropdown
           | items be a 20-headed beast makes everything upstream need to
           | define them first.
           | 
           | Sometimes you just need a form to do what it says on the
           | label.
        
         | scotty79 wrote:
         | I started with a templating system that had a very limited
         | logic and I'm still quite fond of this approach.
         | 
         | Basically arguments for a template had a form of a tree
         | prepared by the controller function. The template could only
         | display values from the tree (possibly processed by some
         | library function, for example date formatter), hide or show
         | fragments of html dependaning on the presence or absence of
         | some value or branch in the tree, descend into a branch of a
         | tree and iterate over an array from the tree, while descending
         | into one iteration at a time. Also could include subtemplate
         | feeding it a branch of the tree. This was enough to build any
         | UI out of components and kept their html simple, separate and
         | 100% html. Even the template logic had a form of html comments.
         | One could open such template in any html editor including
         | visual ones. It was all before advent of client side
         | frameworks.
         | 
         | You could mimic this in React by preparing all data in render
         | method as a jsonlike tree before outputting any JSX tag and
         | limit yourself inside JSX part to just if and map(it =>{}) and
         | single value {}
        
           | noduerme wrote:
           | Yeah. I did this too. Mine's driven off a database with a
           | structure of
           | 
           | 1. pages, each of which may or may not have a parent pageID;
           | the entire tree is read and parsed to generate the menu
           | system and links/sub-links
           | 
           | 2. modules which reference HTML templates to load through a
           | parser that replaces areas with {{handlebar}} variables
           | written by the clients through their own API
           | 
           | 3. something like page_modules which locate the modules on
           | the page,
           | 
           | 4. a renderer which uses the above stuff to make the menu
           | system, figure out what URL people are trying to get to, load
           | the page, load the modules in the page, and fill in the crap
           | the client wrote inside the {{handlebars}}
           | 
           | This has worked so well for so long that I can basically spin
           | up a website in 15 minutes for a new client, let them fill in
           | anything I want them to by themselves, throw some art on it
           | and call it a day.
           | 
           | It worked so well that I ended up writing a newer version
           | that uses a bunch of Javascript to accomplish basically the
           | same thing with smoother transitions in a single-page app
           | that doesn't look or act like an SPA, but it was basically
           | pointless.
        
         | ramblerman wrote:
         | > Don't marry your display code to your business logic.
         | 
         | > It's the reason I never went for JSX or other frameworks that
         | put logical code into templates or things like that
         | 
         | That's not mixing business logic with display code, your
         | confusing (or conflating) 2 entirely different things here.
        
           | noduerme wrote:
           | I don't think I'm conflating things. Any time you insert
           | sugar into your HTML that makes your end user see output
           | that's been inserted by how your framework interprets that
           | sugar, you may be binding your business logic to your display
           | code. Possibly in subtle ways you don't realize until later.
           | A common case is rounding decimals to a user that aren't
           | rounded in the system.
           | 
           | Letting any control flow into your HTMX/JSX templates is
           | begging for trouble. It boggles the mind that people
           | abandoned PHP - where at least the output was static and
           | checkable - for something exactly like mixed HTML/code where
           | some processing was done on the front end and everything was
           | supposed to be dehydrated. Only to pivot again to hydration
           | on the back-end.
           | 
           | JSX and React came on the scene 10 years after I first
           | realized using the client for anything logical was an anti-
           | pattern. Back then, I remember people would write things
           | like:
           | 
           | CustomerFormForClientX extends CustomerFormForClientZ [...]
           | 
           | and put a bunch of validation on the client side, too.
           | Clients are dumb terminals. That's it. I'm not confusing the
           | use of logic-in-JSX with business-logic-in-display. One only
           | has to look at every basic JSX example on the web that's
           | taught to newbies to see a million ways it can go wrong.
        
             | vacuity wrote:
             | As someone who has little experience in this topic but has
             | come to a similar conclusion, I think the main downside of
             | this strict separation you're recommending is
             | performance/efficiency. Have you noticed that to be a
             | problem in practice? It's not always clear whether the
             | simplest solution can actually be feasible, or perhaps that
             | is just a reflection of still untapped understanding of the
             | problem domain.
        
         | nbardy wrote:
         | I think your principle is good, but this is going too far to
         | throw out react.
         | 
         | You really just want to split those things out of the UI
         | component and keep minimal code in the component file. But
         | having it be code free is too much for real client side apps.
         | 
         | Modern apps have very interactive UI that needs code. Things
         | like animation management(stop/start/cancel) etc... have subtle
         | interactions. Canvas zoom and pan, canvas render layouts that
         | relate to UI, etc... lots of timing and control code that is
         | app state dependent and transient and belongs in components.
         | 
         | I apply the simple principle and move any app state management
         | and business logic out of the component UI code. But still lean
         | into logical code in UI.
        
       | thomasjudge wrote:
       | I wish this article had more examples/details; as it is, it is
       | kind of .. abstract
        
       | Darmani wrote:
       | TCP is great. Long chains of one-line functions that just permute
       | the arguments really suck. These both get called abstraction, and
       | yet they're quite different.
       | 
       | But then you hear people describe abstraction _ahem_ abstractly.
       | "Abstraction lets you think at a higher level," "abstraction
       | hides implementation detail," and it's clear that neither of
       | those things are really abstractions.
       | 
       | As the OP mentions, we have a great term for those long chains of
       | one-line functions: indirection. But what is TCP? TCP is a
       | _protocol_. It is not just giving a higher-level way to think
       | about the levels underneath it in the 7-layer networking model.
       | It is not just something that hides the implementations of the IP
       | or Ethernet protocols. It is its own implementation of a new
       | thing. TCP has its own interface and its own promises made to
       | consumers. It is implemented using lower-level protocols, yes,
       | but it adds something that was fundamentally not there before.
       | 
       | I think things like TCP, the idea of a file, and the idea of a
       | thread are best put into another category. They are not simply
       | higher level lenses to the network, the hard drive, or the
       | preemptive interrupt feature of a processor. They are concepts,
       | as described in Daniel Jackson's book "The Essence of Software,"
       | by far the best software design book I've read.
       | 
       | There is something else that does match the way people talk about
       | abstraction. When you say "This function changes this library
       | from the uninitialized state to the initialized state," you have
       | collapsed the exponentially-large number of settings of bits it
       | could actually be in down to two abstract states, "uninitialized"
       | and "initialized," while claiming that this simpler description
       | provides a useful model for describing the behavior of that and
       | other functions. That's the thing that fulfills Dijkstra's famous
       | edict about abstraction, that it "create[s] a new semantic level
       | in which one can be absolutely precise." And it's not part of the
       | code itself, but rather a tool that can be used to describe code.
       | 
       | It takes a lot more to explain true abstraction, but I've already
       | written this up (cf.:
       | https://news.ycombinator.com/item?id=30840873 ). And I encourage
       | anyone who still wants to understand abstraction more deeply to
       | go to the primary sources and try to understand abstract
       | interpretation in program analysis or abstraction refinement in
       | formal verification and program derivation.
        
         | cloogshicer wrote:
         | Hey Jimmy, I've read your comment and also your article in the
         | past with great interest. This topic is absolutely
         | _fascinating_ to me.
         | 
         | I just re-read your article but unfortunately I still struggle
         | to really understand it. I believe you have a lot of experience
         | in this, so I'd love to read a more dumbed down version of it
         | with less math and references to PL concepts and more practical
         | examples. Like, this piece of code does not contain an
         | abstraction, because X, and this piece of code does, because Y.
         | 
         | Keep up the good work!
        
           | blue_pants wrote:
           | Very interesting indeed. +1 for more explanations
           | 
           | For example, in the "TV -> serial number" abstraction, if I
           | were to define only one operation (checking whether two TV's
           | are the same), would it make it a good abstraction, as now it
           | is both sound and precise?
           | 
           | And what are the practical benefits of using this definition
           | of abstraction? Even if I were to accept this definition, my
           | colleagues might not necessarily do the same, nor would the
           | general programming community
        
             | Darmani wrote:
             | > if I were to define only one operation (checking whether
             | two TV's are the same), would it make it a good
             | abstraction, as now it is both sound and precise?
             | 
             | It would!
             | 
             | > And what are the practical benefits of using this
             | definition of abstraction?
             | 
             | Uhhh...that it's actually a coherent definition, and it's
             | hard to think or speak clearly without coherent
             | definitions?
             | 
             | If you're talking about using it in communication, then
             | yeah, if you can't educate your coworkers, you have to find
             | a common language. They should understand all the words for
             | things that aren't abstraction except maybe "concept," and
             | when they use the word "abstraction," you'll have to deduce
             | or ask which of the many things they may be referring to.
             | 
             | If you're talking about using it for programming: you kinda
             | can't not use it. It is impossible to reason or write about
             | code without employing abstraction somewhere. What you can
             | do is get better about finding good abstractions, and more
             | consistent about making behavior well defined on abstract
             | states. If you're able to write in a comment "If this
             | function returns success, then a table has been reserved
             | for the requesting user in the requested time slot," and
             | the data structures do not organize the information in that
             | way, and yet you can comment this and other functions in
             | terms of those concepts and have them behave predictably,
             | then you are programming with true abstraction.
             | 
             | In this case, not programming with true abstraction would
             | mean one of two things:
             | 
             | 1. You instead write "If this function returns success,
             | then a new entry has been created in the RESERVATIONS table
             | that....", or
             | 
             | 2. You have another function that says "Precondition: A
             | table has been reserved for the user in this timeslot," and
             | yet it doesn't work in all cases where the first function
             | returns success
             | 
             | I think it's pretty clear that both ways to not use true
             | abstraction make for a sadder programming life.
        
           | Darmani wrote:
           | Thanks!
           | 
           | I'll have to muse about what the more dumbed down version
           | would look like (as this version is already quite dumbed down
           | compared to the primary sources). It wouldn't be quite a
           | matter of saying "This code contains an abstraction, this
           | other code doesn't," because (and this is quite important)
           | abstraction is a pattern imposed on code, and not part of the
           | code itself.
           | 
           | We do have a document with a number of examples of true
           | abstraction -- written in English, rather than code, in
           | accordance with the above. It's normally reserved for our
           | paying students, but, if you E-mail me, I'll send it to you
           | anyway -- my contact information easy to find.
        
             | cloogshicer wrote:
             | Thanks a lot for the offer, I just emailed you :)
        
       | ChrisMarshallNY wrote:
       | _> a bad one turns every small bug into an excavation._
       | 
       | I find that I need to debug my abstractions frequently, _while
       | I'm first writing my code_ , then I never need to dig into them,
       | ever again, or they do their job, and let me deal with
       | adding/removing functionality, in the future, while not touching
       | most of the code.
       | 
       | That's why I use them. That's what they are supposed to do.
       | 
       | Because they are abstractions, this initial debugging is often a
       | lot harder than it might be for "straight-through" code, but is
       | made easier, because the code architecture is still fresh in my
       | mind; where it would be quite challenging, coming at it without
       | that knowledge.
       | 
       | If I decide it's a "bad abstraction," because of that initial
       | debugging, and destroy or perforate it, then what happens after,
       | is my own fault.
       | 
       | I've been using layers, modules, and abstractions, for decades.
       | 
       | Just today, I released an update to a shipping app, that adds
       | some _huge_ changes, while barely affecting the user experience
       | (except maybe, making it better).
       | 
       | I had to spend a great deal of time testing (and addressing small
       | issues, far above the abstractions), but implementing the major
       | changes was insanely easy. I swapped out an entire server SDK for
       | the "killer feature" of the app.
        
       | voidhorse wrote:
       | The best way to achieve a good abstraction is to recall what the
       | word meant before computer science: namely, something closer to
       | _generalization_.
       | 
       | In computing, we emphasize the communicational (i.e. interface)
       | aspects of our code, and, in this respect, tend to focus on an
       | "abstraction"'s role in hiding information. But a _good_
       | abstraction does more than simply hide detail, it _generalizes_
       | particulars into a new kind of  "object" that is easier to reason
       | about.
       | 
       | If you keep this in mind, you'll realize that having a lot of
       | particulars to identify _shared properties_ that you can abstract
       | away is a prerequisite. The best abstractions I 've seen have
       | always come into being only _after_ a significant amount of
       | particularized code had already been written. It is only then
       | that you can identify the actual common properties and patterns
       | of use. Contrarily, abstractions that are built upfront to try
       | and do little more than hide details or to account for potential
       | similarities or complexity, instead of _actual_ already existent
       | complexity are typically far more confusing and poorly designed.
        
         | 29athrowaway wrote:
         | Computers are to manipulate data.
         | 
         | Data = representations = abstractions
         | 
         | This article is so fundamentally lost that it forgets what
         | computers are for.
         | 
         | Computers exist to implement abstractions.
        
           | dishsoap wrote:
           | Did you even read it?
        
             | 29athrowaway wrote:
             | Unfortunately, I did. It is an attempt to approach
             | complexity, cognitive load and high entropy in code,
             | jumping to conclusions prematurely while suggesting a
             | solution that is worse than the problem.
        
               | f1shy wrote:
               | I would really like to convince you that you are missing
               | something very important. Please try to make sense of the
               | article, reading it again a trying to find cases where it
               | makes sense for you.
        
           | javcasas wrote:
           | Computers are to manipulate data, not to manipulate a pointer
           | to a pointer to a pointer to a pointer to data.
           | 
           | They can do that too, but the cache miss cost already makes
           | that expensive.
        
           | astrobe_ wrote:
           | You are confusing too many things.
           | 
           | "10" is the representation of a data; "0xA" is another
           | representation of the same data.
           | 
           | Not being able to touch something doesn't make it an
           | abstraction. Light is not an abstraction, a contract is not
           | an abstraction. 10 is not an abstraction, it is an ordinal
           | [1].
           | 
           | "Isomorphism" is an abstraction. It doesn't name a particular
           | data or value, but a class of functions that share common
           | properties. A function template or functions written in a
           | dynamically typed language can describe a particular group of
           | isomorphism.
           | 
           | [1] https://en.wikipedia.org/wiki/Set-
           | theoretic_definition_of_na...
        
             | 29athrowaway wrote:
             | Number notations and the glyphs and conventions used in
             | them are abstractions.
             | 
             | 10 in a database record of a warehouse system may be an
             | abstraction for 10 items of a given type in a warehouse.
        
             | JadeNB wrote:
             | > Light is not an abstraction, a contract is not an
             | abstraction. 10 is not an abstraction, it is an ordinal
             | [1].
             | 
             | It seems to me that all of those things very much are
             | abstractions. They are not the utmost level of abstraction,
             | but they are abstractions!
             | 
             | (Actually "a contract," which at first I thought was the
             | clearest win, I'm now not sure about. On reflection, it
             | seems like a concretization, turning abstract ideas of
             | trust and reliability into concrete conditions under which
             | the contract has or has not been met.)
        
           | patrulek wrote:
           | I kind of agree and i find that is why (other) abstractions
           | should be created around data not around behaviours.
        
           | weMadeThat wrote:
           | Ever read any MS Windows code?
           | 
           | The author is relatively clear about good and bad, or rather
           | pointless, abstractions.
        
         | f1shy wrote:
         | This is exactly the point, also in other comments, quoting
         | Dijkstra.
         | 
         | Abstraction not only hide details, but also add semantic value.
        
         | astrobe_ wrote:
         | Yes, abstraction and generalization are properties you'd rather
         | look for _the second time around_. Someone was already warning
         | about this 25 years ago [1]:
         | 
         |  _You have a boring problem and hiding behind it is a much more
         | interesting problem. So you code the more interesting problem
         | and the one you 've got is a subset of it and it falls out
         | trivial. But of course you wrote ten times as much code as you
         | needed to solve the problem that you actually had._
         | 
         |  _Ten times code means ten times cost; the cost of writing it,
         | the cost of documenting it, it the cost of storing it in
         | memory, the cost of storing it on disk, the cost of compiling
         | it, the cost of loading it, everything you do will be ten times
         | as expensive as it needed to be. Actually worse than that
         | because complexity increases exponentially._
         | 
         | This person did his own CAD software from scratch in order to
         | make custom chips [2].
         | 
         | [1] https://www.ultratechnology.com/1xforth.htm
         | 
         | [2] https://en.wikipedia.org/wiki/Charles_H._Moore
        
           | zelphirkalt wrote:
           | Although of course solving the abstract problem does not have
           | to be 10 times as much code. The best solutions are often
           | those, that recognize the more general problem, solve it with
           | little and elegant code, then turn to the specific problem,
           | expressing it in terms of the abstract problem and thereby
           | solving it in just a few lines of code. Such an approach
           | should usually be accompanied by some documentation.
           | 
           | To give a trivial example: Binary search or any other bog
           | standard algorithm. You would want to have an implementation
           | for the algorithm and named as such and then only apply it in
           | the specific case you have. Sorting algorithms. You don't
           | want to rewrite it all the time. Actually rewriting it all
           | the time would be the thing that creates "10 times" the code.
        
             | mattmanser wrote:
             | No. Just no.
             | 
             | This is the exact thought process that leads to unnecessary
             | abstraction. This is the attitude that the article is
             | criticizing.
             | 
             | A good rule of thumb is never abstract unless you genuinely
             | have done the same thing twice already.
             | 
             | i.e. only write an abstraction after you've written the
             | boring, simple, concrete, implementation twice and are
             | about to write it a third time.
        
               | xtracto wrote:
               | I am wondering where the "generalize code after doing it
               | for the 3rd time" rule of thumb comes from? I also
               | subscribe to it, and read it somewhere 15/20 years ago.
               | 
               | Was it the Mythical Man Month book Maybe?
        
               | laserlight wrote:
               | See the C2 Wiki article:
               | https://wiki.c2.com/?ThreeStrikesAndYouRefactor
        
               | 0x696C6961 wrote:
               | The rule of three should obviously not be applied to well
               | known data structures and algorithms ...
        
               | II2II wrote:
               | Well known data structures and algorithms are well know
               | because they have been used more than three times. That
               | said: if you are dealing with a situation where you have
               | to implement them yourself, you may want to consider
               | whether the rule of three applies. (Clearly this depends
               | upon the situation.)
        
               | bluGill wrote:
               | If the well known data structures and algorithms are not
               | provide by your language get a better language.
               | 
               | There are exceptions. If you are implementing the
               | language it is your job to write them. It is useful as a
               | student to implement the basics from scratch. Your
               | language may decide something is not allowed and thus not
               | implement it (a doubly linked list is almost always a bad
               | idea in the real world. Likewise you don't need to
               | provide all the sorting algorithms)
        
               | chthonicdaemon wrote:
               | Tell that to Richard Hamming: "Instead of attacking
               | isolated problems, I made the resolution that I would
               | never again solve an isolated problem except as
               | characteristic of a class." [1]
               | 
               | I have seen this "premature abstraction" warning creep
               | through our discourse lately, but I don't clearly
               | understand it. I feel like I'm making calls all the time
               | about when to introduce functions or classes that will
               | save you time or effort in the future without forcing
               | yourself to see the repetition before you do. Not only
               | that but Hamming's advice has rung true in my career.
               | Solving the general problem is often easier than solving
               | a specific case and can be re-used for later instances,
               | too.
               | 
               | [1] https://jamesclear.com/great-speeches/you-and-your-
               | research-...
        
               | malfist wrote:
               | To your point, we use abstractions all the damn time.
               | They're everywhere. Even programming languages are an
               | abstraction (especially high level ones). You and I, and
               | everybody else here doesn't pick a cylinder and a block
               | to write to and tell the hard drive to move it's arm into
               | place and record the magnetic data, no we all talk about
               | inserting a row into the DB.
               | 
               | Abstractions are essential to productivity or you'll
               | never get out of the "make it from scratch" trap
        
               | sgarland wrote:
               | Yes, but there is a line to be drawn somewhere.
               | Filesystems are sufficiently advanced such that there's
               | no meaningful gains to be had from manually allocating
               | CHS for data, and they provide housekeeping. C abstracts
               | a lot of architecture-specific information away, but
               | still requires that you understand a modicum of memory
               | management; if you understand it (and cache line access)
               | well, you can get even more performance. Python abstracts
               | that away as well, and gives you a huge standard library
               | to accomplish many common tasks with ease.
               | 
               | You can quickly make a prototype in Python, but it won't
               | be as performant as C. You can spend time profiling it
               | and moving computationally-heavy parts into C extensions
               | (I do this for fun and learning), but you'll likely spend
               | more time and get worse results than if you just rewrote
               | it.
               | 
               | Docker is an abstraction over already-existing technology
               | like cgroups. It provides an easy-to-understand model,
               | and a common language. This is quite valuable, but it
               | does allow one to not know about what it's hiding, which
               | is problematic when troubleshooting - for example,
               | naively assuming that querying /proc in a container shows
               | the CPU resources allocated to the container, rather than
               | the host.
               | 
               | That's how I view abstractions. They can be incredibly
               | useful, but they usually have trade-offs, and those
               | should always be considered. Most importantly, you should
               | at a minimum be aware of what you're giving up by using
               | them, even if you don't fully understand it.
        
               | coliveira wrote:
               | > Solving the general problem is often easier than
               | solving a specific case and can be re-used for later
               | instances, too.
               | 
               | You and the other person are both correct. What you're
               | saying makes sense and it is what everybody is trained to
               | do. However, it leads to a lot of useless code exactly
               | because you're applying an abstraction that is used only
               | once. That's why most codebases are bloated and have a
               | huge number of dependencies.
        
               | nyrikki wrote:
               | Protecting yourself from technologies, vendors, etc...
               | are all very very worth making abstractions for.
               | 
               | The silly "only after # Times" type rules stolen from the
               | DRY for DRY reasons is part of the problem and far from
               | the solution.
               | 
               | "Abstraction" could be anything from keeping code in
               | different but adjacent files to an entire anti-corruption
               | layer etc....
               | 
               | The costs are different, as are the benefits.
               | 
               | It is horses for courses, not 'one rule that the
               | government doesn't want you to know about'
        
               | jcelerier wrote:
               | At no point in my life my code has been made worse
               | because I've used an existing sort function instead of
               | writing a buggy one
        
               | SleepyMyroslav wrote:
               | If we ignore 'buggy' part I think you projecting current
               | good state back into not so good old times. I am pretty
               | sure you will not replace radix sort that uses domain
               | knowledge of reduced value range with qsort circa C++98.
               | 
               | Things became much better after relatively recent
               | improvements in generalist sorting libraries: when
               | pattern defeating quick sort variants became norm, when
               | mostly sorted cases got covered ...
               | 
               | Tbh I do have a case from 2013-16 when I regret not doing
               | it for one AAA project on ps4.
        
               | tines wrote:
               | It's interesting when people say "No. Just no." and then
               | go on to explain. Wasn't the response supposed to be
               | "just" a no?
        
           | dustingetz wrote:
           | imo more like the 50th time around, and by a computer
           | scientist, and not on company time until after the
           | abstraction POC is validated, for example both React and
           | Angular were side projects before the firm decided to invest.
           | Software development outcomes today are driven by: ignorance,
           | narcissism, and self preservation
        
           | Sesse__ wrote:
           | Have you ever looked at how useless Chuck Moore's stuff is?
           | Like, the chip designs are of the type "384 independent Forth
           | chips with tiny amounts of RAM and mediocre interconnects,
           | and if you want to actually do anything with them, you'll
           | need to use 128 of them to program your own DDR3 controller".
           | Or, he demonstrates how awesome Forth is by showing that you
           | can do "the guts of a disk driver" in five lines, except that
           | it's the dog-slow PIO mode.
           | 
           | It turns out that if you can just change the problem
           | statement, then sure, you can write very simple things. But
           | if you have a real problem to solve (and you can't just say
           | "no, I want to solve a simpler problem"), the Chuck Moore way
           | of thinking doesn't really produce, well, good solutions. It
           | simply doesn't scale to anything large, and not everything
           | can be made small.
           | 
           | https://yosefk.com/blog/my-history-with-forth-stack-
           | machines... (2010) is a fairly interesting experience from
           | someone on the outside trying to work in the same way. It...
           | didn't work that well.
        
             | PaulHoule wrote:
             | I wrote a little chess engine in Python that plays at least
             | elo 1400 using alpha-beta search with the goal of making it
             | simple and pedagogical, trying to outdo Lisp. I am thinking
             | about making it talk XBoard, removing the experimental
             | stuff, then posting it to GitHub just as a nice example.
             | 
             | I think though if I want to get more into chess programming
             | I'm going to switch to a faster language.
        
             | lebuffon wrote:
             | IMHO that is a failed example of "Chuck Moore's stuff". He
             | went down a rabbit hole to an extreme level because that's
             | what he does. His earlier CPU experiments like the 4016 and
             | Shboom were excellent examples of ultra-RISC architectures.
             | 
             | The thing Chuck explored, related to abstraction, which I
             | don't see much in conventional machines was reducing
             | calling overhead. (1 cycle call and intrinsic return on
             | many instructions ie: free return)
             | 
             | Some of the decisions we make today have a lot to do with
             | what happens at the hardware level when we add abstraction.
             | It just costs more than we are prepared to pay so it is
             | avoided ... but for the wrong reason.
        
             | DonHopkins wrote:
             | The real time visual mixing console that produced many
             | music videos that ran endlessly on MTV back when they
             | actually played music videos, and special effects for
             | blockbuster films like RoboCop and Total Recall, wasn't a
             | "simple thing".
             | 
             | https://news.ycombinator.com/item?id=29261868
             | 
             | DonHopkins on Nov 18, 2021 | prev | next [-]
             | 
             | Coco Conn and Paul Rother wrote this up about what they did
             | with FORTH at HOMER & Assoc, who made some really classic
             | music videos including Atomic Dog, and hired Charles Moore
             | himself! Here's what Coco Conn posted about it, and some
             | discussion and links about it that I'm including with her
             | permission:
             | 
             | Peter Conn:
             | 
             | https://web.archive.org/web/20230516102928/https://imgur.co
             | m...
             | 
             | Homer & Associates (1982):
             | 
             | http://leftbrain.us/rotherHistory/homer.html
             | 
             | Peter Conn Papers at Stanford:
             | 
             | https://web.archive.org/web/20190603111701/https://library.
             | s...
             | 
             | https://oac.cdlib.org/findaid/ark:/13030/c8n303pn/entire_te
             | x...
             | 
             | George Clinton - Atomic Dog (Official Music Video) HD
             | 
             | https://www.youtube.com/watch?v=LMVZ36VA0wg
             | 
             | Steve Miller Band - Abracadabra
             | 
             | https://www.youtube.com/watch?v=tY8B0uQpwZs
             | 
             | Steve Miller Band - Bongo Bongo
             | 
             | https://www.youtube.com/watch?v=_NrsRZdMI-A
             | 
             | Flying Logos for 1989 Siggraph Electronic Theater:
             | 
             | https://www.youtube.com/watch?v=9hIOfEiy4lc
             | 
             | >First shown at the 1989 Siggraph Electronic Theater to a
             | rave response, this 3 minute humourous film went on to win
             | several top computer graphic awards that same year
             | including Niccograph of Japan.
             | 
             | >Coco: This was a show favorite at the SIGGRAPH film show
             | that year. The year before the conference committee decided
             | that showing demos wasn't the way to go anymore. Peter
             | wrote Flying Logos as a way to sneak our demo reel into the
             | show by turning it into a story. It worked and we made it
             | into the film show.
             | 
             | >Don: I truly believe that in some other alternate
             | dimension, there is a Flying Logo Heaven where the souls of
             | dead flying logos go, where they dramatically promenade and
             | swoop and spin around each other in pomp and pageantry to
             | bombastic theme music. It would make a great screen saver,
             | at least! Somewhere the Sun Logo and the SGI Logo are still
             | dancing together.
             | 
             | ----
             | 
             | Peter Conn and I [Coco Conn] had a company called HOMER &
             | Assoc. which was located at the Sunset Gower Studios from
             | 1977 until we closed shop in 1997. We made music videos,
             | commercials & computer graphics/special effects for feature
             | films. One cool note, we worked with Paul Verhoven on both
             | RoboCop in 1986 and the x-ray scene for Total Recall in
             | '89.
             | 
             | HOMER was actually a real time visual mixing console that
             | our in-house engineer spent 1978 - 1981 designing and
             | building, from scratch. The name HOMER stood for "Hybrid
             | Optical Montage Electronically Reproduced." I helped as
             | well, soldering the LEDs on the console and running cables.
             | Peter built his own optical printer and three years into
             | the build we also bought an early computer paint system.
             | Our engineer finished building the console and promptly
             | decided to move to England. We hadn't used it because we
             | still hadn't found the right software to run the system.
             | Luckily that's when Paul Rother joined the company.
             | 
             | The joy stick on our console would bump you to the next
             | line of code (being a command or sequence of events: fade,
             | cut, dissolve, etc.) The console had touch sensitive fader
             | pads. There were no dials. I think they were made by
             | Allison? Each channel (which controlled either a slide
             | projector or a film projector) was touch sensitive. After
             | recording a sequence we could then tweek the current
             | version using additional effects the channels offered such
             | as momentary, additive, on/off, etc. For instance if you
             | wanted to crossfade two images, you could either program it
             | or perform it. Of course everything you did was recorded
             | and would play back on the next round. You literally
             | performed a sequence of visual effects with your hands.
             | Peter would do countless passes until everything was
             | perfect. This performance would then be played back to IP
             | film on the optical printer. Each slide tray or film real
             | would be individually run, one by one, to IP film.
             | Sometimes there would be 10-15 or more passes to get all
             | the elements transferred. Once that was done we would then
             | convert the IP film to video and do additional video
             | editing and effects. A totally nuts analogue system. But it
             | worked.
             | 
             | ---------------
             | 
             | HOMER Explained by Paul Rother, in-house programmer,
             | (1982):
             | 
             | The photo is Paul sitting in front of the Optical Printer
             | 7-bit Paint system, Homer and Associates, circa 1982. Homer
             | and Associates was really one of a kind kinda of company.
             | Founded by Peter Conn, originally I got hired to program
             | Homer II, a visual realtime mixing console. Homer I is
             | another whole story, but before my time. Homer II consisted
             | of 16 slide projectors, 4 movie projectors, a 4 track tape
             | recorder, 24 visual channels (each with its own Z80) touch
             | sensitive sliders, a master Z80 S100 bus system and
             | featuring "the joy stick bumper " control, which looked
             | liked the gear shift right out of a 1964 mustang
             | convertible.
             | 
             | The idea was that you would program a visual sequence, then
             | play the sequence in sync with the sound track on the
             | joystick, including cascades, bumps, cuts, etc. The whole
             | thing would be recorded, and if you wanted to, like an
             | audio mixer, go back and do over dubs, making corrections.
             | Then once you had the perfect "hero" recording, you take
             | the 8" floppy disc with the hero recording and the trays of
             | slides to the optical printer, and record it to IP motion
             | picture film, making multiple passes, one tray at a time.
             | Now that I think about it, it was a crazy idea. We actually
             | got the whole thing to work. And it worked great!
             | 
             | Forth & Charles Moore
             | 
             | We hired Forth, Inc. and got Charles Moore, the inventor of
             | FORTH to program the console host computer. I learned FORTH
             | and worked with Charles. I programmed the 2K byte EPROM in
             | each visual channel. On the Master Z80 system we ran
             | PolyForth a multi tasking system in 32K bytes. We had an
             | extra 16K RAM for buffers and things. If I remember right,
             | the system ran four tasks, but that was 20 years ago, my
             | memory may be hazy.
             | 
             | Anyway, I learn not only FORTH from Charles Moore, but also
             | how to factor code in to small reusable routines, WORDs
             | they're called in FORTH. I learned Object Oriented
             | Programming without knowing it. Also a lot of use of
             | vectors. Its a cool language. Charles Moore was a great
             | inspiration to me, and really taught me a great deal that
             | they never taught me in computer programming school.
             | 
             | CAT-700
             | 
             | After we got the basic Homer II working and were able to
             | record on the optical printer, Peter had another idea. He
             | wanted to be able to see the movement of the optical
             | printer, and see a prior frame compared to the current
             | frame. We already had a video assist on the Fries Mitchell
             | 35mm. What we needed was a Frame Buffer. We heard of S100
             | video board called the CAT-100, which was 1-bit frame
             | buffer, good enough for what we needed. Somehow we never
             | found a 1-bit version, but we found 7-bit version in the
             | recycler!
             | 
             | We flew to Reno, rented a car and drove to a log cabin up
             | in the hills of Truckie California. We got a demo of the
             | thing. The guys were super secret and didn't want us to see
             | the controlling program. It worked, so we bought it, and
             | then flew onto Palo-Alto and met the French guy who
             | designed it. They checked it out and it was OK. This was
             | the days before computer designed boards, and all the
             | traces on the board were curvy, kinda like a Van Gogh
             | painting. We learned that it was 7-bit (CAT-700) because it
             | would have been an 8-bit, but they could not get the 8th
             | bit to work. We spent the night in Palo Alto with a
             | Stanford friend of Peters working on a crazy secret Apple
             | project, the Lisa. 32KByte Paint System
             | 
             | So I got the CAT-700 frame buffer to work, programmed in
             | FORTH. So in that 32K we had an optical printer control
             | system, and a paint system, all in one. (Also the OS,
             | compiler, debugger, etc.) We later hooked up a Summigraphic
             | Bitpad (before the Watcom tablet) and were able to draw on
             | top of digitized frames. It got to the point where we
             | needed TWO optical printers, one to digitize from film, and
             | the other to record to film. Rube Goldberg is not strong
             | enough descriptive to describe the system, with the filter
             | wheels and all on stepper motors, it made music. The first
             | use of the system was effects for Steve Miller Music Video,
             | Abracadabra. I also remember using it on the George Clinton
             | Video, Atomic Dog.
             | 
             | This photo was taken right after we got the system to work.
             | I had hooked up an analog slider box, which controlled
             | things like color. There were 4 color maps we could switch
             | between instantly We did a lot of work in planes, using 2
             | planes for the original image to be rotoscoped, and the
             | other 5 planes to draw onto. This photo was taken for an
             | article in Millimeter Magazine. The photo ended up being a
             | two page color spread, and I think Peter was pissed, cause
             | I got premier exposure.
             | 
             | TTL logic
             | 
             | At Homer and Assoc. I also learned TTL logic and designed a
             | number of computer boards for the S100 bus. One that
             | controlled stepper motors with a timer chip (Motorola
             | 6840). Another to control the Slide Projectors also using
             | the same Motorola timer chip to control the lamp triacs. My
             | favorite thing, about the system, was the use of the
             | cassette storage interface as a cheap timecode
             | reader/writer.
        
             | tikhonj wrote:
             | I did some work with his Greenarrays chip as a student. It
             | was very limited and awkward to use but it could also
             | operate on _absurdly_ low energy, with very low overhead
             | for waking up or almost completely powering down. At some
             | point, we had a demo running some simple signals processing
             | code off a homemade bleach battery, and I wouldn 't be
             | surprised if you could make something work off a bunch of
             | lemons too.
             | 
             | This was over a decade ago (yikes) and I don't remember the
             | exact numbers, but I do remember it used substantially less
             | power than a comparable MSP430 microcontroller.
             | 
             | That seems pretty useful and impressive to me, especially
             | given it was created by a very small team with limited
             | funding.
        
             | samatman wrote:
             | Useless?
             | 
             | Like the RTX2000 which landed on a comet kind of useless?
             | 
             | Or do you mean some other kind of useless. Maybe the
             | controlling radio telescopes kind of useless. That must be
             | it.
        
               | Sesse__ wrote:
               | > Like the RTX2000 which landed on a comet kind of
               | useless?
               | 
               | Yeah, in 1983 he designed a chip that was further
               | developed by others for space usage.
               | 
               | > Maybe the controlling radio telescopes kind of useless.
               | 
               | Yeah, which he did in 1970.
               | 
               | Note a pattern here? That this design paradigm holds up
               | pretty well in a primitive computing world when things
               | are simple and demands are low, and is thoroughly useless
               | to keep on promoting today?
        
           | Izkata wrote:
           | While I generally agree with that, the problem is teams,
           | mixes of skill levels, and future time constraints. There's
           | no guarantee the person doing the second implementation knows
           | about the first one, realizes they can be pulled into a
           | better abstraction, or has the time to do it. On the
           | flipside, it's possible the other person (or the first person
           | with more experience) comes up with something better than the
           | first possible version of abstraction. So it ends up being a
           | trade-off you have to decide on at the start, rather than a
           | simple rule, based on team experience and how big (or small
           | [0]) the abstraction actually us.
           | 
           | [0] https://www.folklore.org/Negative_2000_Lines_Of_Code.html
        
           | JoeAltmaier wrote:
           | Ten times? Foo. Maybe double.
           | 
           | But that's still twice the cost, for only a potential win.
           | Better check first that it helps somebody write better/more
           | correct/quicker code.
           | 
           | E.g. we wanted to change our text-screen video driver to
           | allow a full-screen overlay (many years ago). The programmer
           | assigned the task was changing the text-blit code to know
           | about certain lines on the screen that were going to be the
           | 'nested' window, which could be 'up' and masking a subset of
           | the window below, or 'down' and the full screen should be
           | displayed as normal.
           | 
           | He'd been hacking away, changing every case in the code to
           | 'know about' the particular window that was planned. Pulling
           | his hair out, getting nowhere.
           | 
           | I suggested a simple indirection buffer, where each line of a
           | virtual display was pointed to indirectly. To put the
           | subwindow 'up' or 'down' you just pointed those lines of the
           | main screen to a buffer, and pointed the lines of the
           | subwindow to the hardware store for video text.
           | 
           | All his changes folded into 'access display indirectly'.
           | Trivial. Then the up/down code became a loop that changed a
           | pointer and copied some text.
           | 
           | That was actually an abstraction, and actually much
           | shorter/faster/simpler to understand and change.
           | 
           | Later we could change the window dimensions, have multiple
           | windows etc with no change to the video driver.
        
         | themadsens wrote:
         | Well said! I have met a few of these codesmells where the
         | actual functioning is hidden behind a bewildering maze of
         | facades, shims, proxies and whatnot.
         | 
         | I guess some has had an irresistible itch to use as many
         | patterns from the GoF book as possible.
        
           | xtracto wrote:
           | Smells like Ruby to me haha. I know is not the language, but
           | for some reason the ruby code I've stumbled into are all like
           | that.
        
             | vidarh wrote:
             | To me it sounds like what I use Ruby _to get away from_.
             | 
             | It's rare to need facades, proxies, and shims in a
             | dynamically typed language where the caller doesn't need to
             | care about the type of the object they call.
             | 
             | In fact, most of the Gang of Four design patterns either
             | make no sense in Ruby or are reduced to next to nothing.
        
         | antfarm wrote:
         | _The best abstractions I 've seen have always come into being
         | only after a significant amount of particularized code had
         | already been written. It is only then that you can identify the
         | actual common properties and patterns of use._
         | 
         | Early Ruby On Rails comes to mind as a great generalization of
         | web applications in the era of Web 2.0.
        
         | moomin wrote:
         | Generalisation is definitely a good approach, but it's not the
         | only one. Another is "conceptualisation" or reducing
         | repetition: if you find that large numbers of functions are
         | taking the same three parameters, especially if they're using
         | them in similar ways, that's a good sign there's a concept you
         | can introduce that makes those observations explicit.
        
         | layer8 wrote:
         | To put it in slightly simpler terms, abstractions are generally
         | to separate the "what" from the "how".
         | 
         | Functions are the fundamental mechanism for abstraction in
         | computing, which demonstrate that very well, in their
         | separation between interface and implementation. The function
         | signature and associated interface contract represent the
         | "what", and the function's implementation the "how". If the
         | effective (often non-explicit) interface contract relies on
         | most aspects of the actual implementation, then the "what" is
         | almost the same as the "how", and there is little abstraction.
         | The greater the difference between the "what" and the "how",
         | the more of an actual abstraction you have.
         | 
         | This relates to Ousterhout's notion of "deep modules", which
         | are modules whose interface is much simpler than their
         | implementation. In other words, the "what" is much simpler than
         | the "how", which makes for a good abstraction.
         | 
         | It's true that often one has to first implement the "how",
         | possibly multiple times, to get a good notion of which aspects
         | are also still important to the "what", and which aren't.
         | 
         | Note also that generalizations go two ways: The caller needs
         | less knowledge about the implementation, but it also means that
         | the caller can rely less on the properties of a concrete
         | implementation. This is again well reflected in function types.
         | A (pure) function _g_ : _A_ - > _B_ is a generalization of a
         | function _f_ : _C_ - > _D_ only if _A_ is a subtype
         | (specialization) of _C_ and _B_ is a supertype (generalization)
         | of _D_. Here _D_ could expose properties of the function's
         | implementation ( _f_ ) that _g_ wants to hide, or abstract
         | from.
        
         | marcosdumay wrote:
         | IMO, I dislike that kind of mixing several concepts into the
         | same name that software engineering is full of.
         | 
         | Abstraction means removing details from the thing you present.
         | Generalization means making the same representation valid for
         | several different things.
         | 
         | Are abstractions that don't generalize valuable? Well, maybe
         | there is something better to be found, but those are the bread-
         | and-butter of software engineering; they are what everybody
         | spends almost all of their time writing.
         | 
         | Are generalizations that don't abstract valuable (or even
         | possible)? Well, not if they don't abstract at all, but there
         | are plenty of valuable generalizations that abstract very
         | little. Hell, we have all those standardizing organizations
         | that do nothing more than creating those.
         | 
         | Are the best interfaces the ones that achieve most of both of
         | those? Honestly, I have no idea, there are other goals and if
         | you optimize to extreme levels, they start to become
         | contradictory.
        
           | gspencley wrote:
           | I would warn against conflating the concept of an interface
           | with an abstraction just as much as I would against
           | conflating generalizations and abstractions.
           | 
           | An interface often accompanies an abstraction, sometimes even
           | represents one. But if we get down to definitions, an
           | interface is merely something that you interact with: a
           | function, a class, a network endpoint etc. If you write in
           | machine code, you might not think that you are working with
           | any kind of an interface, and certainly it's not a high level
           | one, but you are interfacing with the machine's native
           | instruction set. You could then argue that the instruction is
           | an abstraction that hides what the machine is capable of. But
           | now we're splitting hairs and about to ask silly
           | philosophical questions like whether a car's steering wheel
           | qualifies as an abstraction that exists to hide the axles. I
           | would argue not. The interface's primary responsibility is to
           | provide a mechanism of interaction, rather than to reduce
           | complexity (though a good interface is simple and intuitive).
           | 
           | Both you and the author of the article posit a similar
           | definition of 'abstraction'. From the article:
           | 
           | > An abstraction is only as good as its ability to hide the
           | complexity of what lies underneath.
           | 
           | And from your comment:
           | 
           | > Abstraction means removing details from the thing you
           | present.
           | 
           | I would actually argue that both, while very close, are
           | missing the mark.
           | 
           | An abstraction exists to reduce a concept down to its
           | essentials.
           | 
           | This doesn't necessarily contradict the definitions offered,
           | but I think there is a nuance here that, if missed, causes
           | the offered definitions to become useless.
           | 
           | The nuance is in deciding what is essential or necessary. If,
           | definitionally, you choose to dispense with acknowledging the
           | essential... well then you get the problems that the author
           | is writing about. You get shit abstractions because no one
           | bothered to think in terms of what they INCLUDING rather than
           | DISPENSING with.
           | 
           | Yes, obviously, we abstract in an attempt to simplify. But
           | that simplification needs to come from a positive rather than
           | a negative.
           | 
           | In other words: What is the thing for? What does it do?
           | 
           | "Hides complexity" is the shittiest answer that anyone could
           | ever offer as a response when faced with a given problem.
           | First, what is the complexity that we are trying to reduce?
           | Secondly, why are we trying to reduce it? Thirdly, when we
           | have achieved our primary goal of reducing the complexity,
           | then what does the thing look like? What value does it offer
           | over interfacing directly with the "thing" being abstracted?
           | 
           | Abstractions in computer science are extremely valuable. So
           | valuable, I would offer, that any time we hear engineers
           | decry abstractions or point to abstractions as the root of
           | all evil we ought to ask a very pressing question: "who hurt
           | you?"
           | 
           | A good engineering solution is a simple one, and an
           | abstraction is intended to be a simpification mechanism. But
           | it doesn't have to necessarily simplify the intuitive
           | understanding of the problem at hand. This is a good goal if
           | you can achieve it, don't get me wrong. But that abstraction
           | might exist so you can swap vendors in the future if that's a
           | business priority. Or because you've identified some other
           | element / component in your system that will be difficult to
           | change later. So you stick it behind an abstraction and
           | "Program to Interfaces" rather than "implementations" so that
           | you can simplify the process of change down the road, even if
           | it comes at an more immediate cost of making the code a bit
           | less intuitive today.
           | 
           | Everything in software is tradeoffs. And a good abstraction
           | exists so that we can focus on requirements and programming
           | to those requirements. This is a smiplification when done
           | properly.But the focus ought to be on defining those
           | requirements, of asking "what should simple look like?"
        
             | vacuity wrote:
             | I agree with what you're saying, and I would phrase it as
             | "write the abstractions that reflect the essential
             | complexity". The whole program should minimally reflect the
             | essential complexity of the problem. Of course actually
             | doing that isn't _easy_ , but the result is obviously a
             | simple solution for a given problem. It becomes another
             | challenge to maintain and refactor: the question of
             | changing problem constraints and being able to minimally
             | change a program to match.
             | 
             | Why are ADTs like stacks, queues, and hashmaps so popular?
             | Why are languages like C or Forth so highly praised for a
             | high ceiling for performance and efficiency? Because they
             | are usually "about as good as it gets" to solve a problem,
             | "what you would've more or less done anyways". Maybe on a
             | GPU, a language like C isn't quite fit, because the problem
             | has changed. Make tools (e.g. CUDA) that reflect that
             | distinct complexity.
        
         | fsndz wrote:
         | this feels like you are talking about langchain
         | haha:https://medium.com/@fsndzomga/jailbreaking-langchain-
         | ad9415a...
        
         | highfrequency wrote:
         | Great point, and agree that generalization makes for the
         | clearest wins from abstraction.
         | 
         | But there are also cases where there is no generalization, but
         | the encapsulation / detail hiding is worthwhile. If you have a
         | big function and in the middle of it you need to sort some
         | numbers, you would probably implement a Sort routine to make
         | the control flow much easier to understand - even if you only
         | use the function once (let's pretend there's no sort
         | functionality in standard library).
         | 
         | Curious if others agree, and what heuristics you use to decide
         | when implementation encapsulation is worthwhile.
        
       | mannyv wrote:
       | Abstraction hides detail, but at what coat?
       | 
       | A network close call at a high level closes a network socket. But
       | at the tcp level there's a difference between close and reset.
       | Which do you want? Your api has removed that choice from you, and
       | if you look you will have no idea if rhe close api does a close
       | or a reset.
       | 
       | Is the difference important? If depends. If you have a bunch of
       | half open sockets and run out of file descriptors then it becomes
       | very important.
       | 
       | Another example: you call read() on a file, and read 10k bytes.
       | Did you know your library was reading 1 byte at a time
       | unbuffered? This abstraction will/can cause massive performance
       | problems.
       | 
       | My favorite one is when a programmer iterates over an ORM-enabled
       | array. Yes, let's do 50,000 queries instead of one because
       | databases are too complicated to learn.
       | 
       | Just like any tool, abstraction has costs and benefits. The
       | problem is that lots of people ignore the cost, and assume the
       | benefit.
        
         | atomicnumber3 wrote:
         | I think an important distinction is hiding details from other
         | parts of the program, and having details being hidden from you.
         | 
         | 99.9% of the time I don't care what the tcp socket closes with
         | as long as it isn't leaking a resource.
         | 
         | And if I did care, then I picked the wrong level of network
         | abstraction to engage with. I should've used something more
         | raw.
         | 
         | Regarding ORM arrays. I have myself recently debugged such a
         | case. I chortled a bit at the amateur who wrote the code (me
         | last year) and the schmuck who accidentally wrapped it in a
         | loop (me 3 weeks ago). Then I changed it slightly to avoid the
         | N queries and went on with my day. No need to lambast the
         | tooling or the programmers. Just write something maintainable
         | that works. No need to throw the entire ORM away just because
         | we accidentally made a web page kinda slow that one time.
         | 
         | And don't worry, I too lament when web pages I don't control
         | are slow. You may rest uneasily knowing that that page would be
         | slow regardless of whether ORMs existed because it is not slow
         | because of ORMs, but because there is no incentive for the
         | business to care enough to make it faster.
        
         | f1shy wrote:
         | >> But at the tcp level there's a difference between close and
         | reset. Which do you want? Your api has removed that choice from
         | you, and if you look you will have no idea if rhe close api
         | does a close or a reset.
         | 
         | If you are doing a ,,TCP application" of course it makes no
         | sense to abstract the TCP layer. Is not about cost. Now if you
         | have an application that has to communicate _somehow_ with
         | other system, and you want to not depend on specific protocols,
         | then the communication part should abstract away that part.
         | 
         | How to deal with your example? Well, if you can say "I will
         | always want X, you can make a configuration option "TCP.close"
         | or "TCP.reset". If "it depends" then you have to build the
         | logic for the selection in the abstraction layer, which keeps
         | hidden.
        
         | josephcsible wrote:
         | > Your api has removed that choice from you
         | 
         | It has? Can't you rely on connect(2) with AF_UNSPEC doing a
         | RST, and shutdown(2) with SHUT_WR doing a FIN?
        
       | noodletheworld wrote:
       | Pretty easy to give generic advice without examples.
       | 
       | "Write more tests, but not too many"
       | 
       | "Use good abstractions where appropriate?"
       | 
       | "The next time you reach for an abstraction, ask yourself: Is
       | this truly simplifying the system? Or is it just another layer of
       | indirection?"
       | 
       | It's easy to create a strawman here (the FactoryAdaptorMapper or
       | whatever) but in reality this kind of generic advice doesn't help
       | anyone.
       | 
       | Of _course_ people want to use good abstractions.
       | 
       | That's _not_ the problem.
       | 
       | The problem is being able to tell the difference between generic
       | arbitrary advice (like this post) and how _your specific code
       | base_ needs to use abstractions.
       | 
       | ...and bluntly, the only way to know, is to either a) get
       | experience in the code base or b) read the code that others have
       | left there before you.
       | 
       | If it's a new project, and you're not familiar with the domain
       | you'll do it wrong.
       | 
       | Every. Single. Time.
       | 
       | So, picking "good" abstractions is a fools game.
       | 
       | You'll pick the wrong ones. You'll have to refactor.
       | 
       | That's the skill; the advice to take away; how to peel back the
       | wrong abstraction and replace it with your next best guess at a
       | good one. How to read what's there and understand what the smart
       | folk before did and why.
       | 
       | ...so, I find this kind of article sort of arrogant.
       | 
       | Oh, you want to be a great programmer?
       | 
       | Just program good code. Use good abstractions. Don't leave any
       | technical debt. Job done!
       | 
       | ...a few concrete examples would go a long way here...
        
         | jchmbrln wrote:
         | I can see the value of examples, but in this case I appreciate
         | the post largely for its universality and lack of examples. On
         | reading it, examples from past and present experience spring
         | immediately to mind, and I'm tucking this away as a succinct
         | description of the problem. Maybe I can share it with others
         | when more concrete examples come up in future code review.
         | 
         | A principle takes skill the apply, but it's still worth stating
         | and pondering.
        
           | noodletheworld wrote:
           | > examples from past and present experience spring
           | immediately to mind
           | 
           | Examples of what?
           | 
           | Picking the wrong abstraction? Regretting your mistakes?
           | 
           | I can certainly think of many examples of that.
           | 
           | How you unwrapped an abstraction and made things better by
           | removing it?
           | 
           | I have dozens of battle stories.
           | 
           | Choosing not to use an abstraction because it was
           | indirection?
           | 
           | Which is what the article says to do?
           | 
           | I'm skeptical.
           | 
           | I suspect you'll find most examples of that are _extremely_
           | open to debate.
           | 
           | After all, you _didn 't_ use the abstraction so you _don't
           | know_ if it was good or not, and you can only _speculate_
           | that the decision you made was actually a good one.
           | 
           | So, sharing that experience with others would be armchair
           | architecture wouldn't it?
           | 
           | That's why this article is arrogant; because it says to make
           | decisions based on gut feel without actually justifying it.
           | 
           | "Is this truly simplifying the system?"
           | 
           | Well, is it?
           | 
           | It's an enormously difficult question to answer.
           | 
           |  _Did_ it simplify the system _after doing it_ is a much
           | easier one, and again that should be the advice to people;
           | 
           | Not: magically do the right thing somehow.
           | 
           | Rather: here is how to undo a mistake.
           | 
           | ...because fixing things is a more important skill and
           | (always) _magically_ doing the right thing from the start is
           | impossible; so it's meaningless advice.
           | 
           | That's the problem with universal advice; it's impossible to
           | apply.
        
         | mrkeen wrote:
         | It might not give enough examples to show you how to do
         | something right, but I think it's enough to warn against doing
         | something wrong, namely introducing an indirection that doesn't
         | provide any of the benefits of abstraction.
         | 
         | My colleagues invariably refer to indirections as abstractions,
         | and it's a frustrating sort of name-squatting, because you
         | can't usefully discuss the tradeoffs of abstractions if they're
         | actually talking about indirections.
         | 
         | That said, the article does drop the ball by seeming to use the
         | terms interchangeably.
        
       | ericflo wrote:
       | Classic post in this genre:
       | https://web.archive.org/web/20151217104831/https://zedshaw.c...
        
       | mixermachine wrote:
       | Reminds me of an old Java Android project I encountered.
       | 
       | EVERY class implemented an interface. 98% of interfaces had one
       | implementation.
       | 
       | Every programmer was applying a different programming pattern. A
       | lot of abstractions seemed incomplete and did not work.
       | 
       | Proguard (mostly used for code obfuscation for Android apps)
       | definitions were collected in the top module even though the
       | project had multiple modules. Half of the definitions were no
       | longer needed and the code was badly obfuscated. Problems were
       | solved by continuesly adding classes and checking what sticks.
       | 
       | The UI was controlled by a stateful machine. State transitions
       | were scatter everywhere in the code with lots of conditions in
       | unforeseen places.
       | 
       | Legacy code was everywhere because no one wanted to risk a very
       | long debugging session of an unforseen change.
       | 
       | No API definitions. Just Maps that get send via REST to URLs.
       | 
       | By biggest mistake was to not directly rewrite this project when
       | I entered the team. We did after one year.
        
       | anonytrary wrote:
       | I'm not sure what it's called (abstraction vs. indirection) but I
       | dislike when everything needs a class/object with some odd
       | combination of curried functions. Some programming languages
       | force this on you more than others I think? As a contrived
       | example
       | "StringManager.SlicingManager.sliceStringMaker(0)(24)(myStr)",
       | I've seen code that reminds me of this and wonder why anyone uses
       | a language where this not only an acceptable idiom, but a
       | preferred one.
        
         | Jaxan wrote:
         | In Haskell gMaker(0)(24)(myStr) and gMaker(0, 24, myStr) would
         | have the same syntax, namely gMaker 0 24 myStr. So that solves
         | the issue.
        
       | aktenlage wrote:
       | Interesting read, although I don't agree with everything. I like
       | the distinction between different qualities of abstractions, made
       | in the beginning. The following bashing of abstractions is too
       | generalized for my taste.
       | 
       | The best part comes close to the end:
       | 
       | > Asymmetry of abstraction costs
       | 
       | > There's also a certain asymmetry to abstraction. The author of
       | an abstraction enjoys its benefits immediately--it makes their
       | code look cleaner, easier to write, more elegant, or perhaps more
       | flexible. But the cost of maintaining that abstraction often
       | falls on others: future developers, maintainers, and performance
       | engineers who have to work with the code. They're the ones who
       | have to peel back the layers, trace the indirections, and make
       | sense of how things fit together. They're the ones paying the
       | real cost of unnecessary abstraction.
        
       | eunonia12 wrote:
       | specify
        
       | ozim wrote:
       | Lots of crud apps add 3-tier architecture that end up something
       | that could be 2 tier.
       | 
       | People add it just in case but the case never materializes- for
       | some probably do but ones I worked with not.
        
       | pdpi wrote:
       | > Think of a thin wrapper over a function, one that adds no
       | behavior but adds an extra layer to navigate. You've surely
       | encountered these--classes, methods, or interfaces that merely
       | pass data around, making the system more difficult to trace,
       | debug, and understand. These aren't abstractions; they're just
       | layers of indirection.
       | 
       | "No added behaviour" wrapper functions add a lot of value, when
       | done right.
       | 
       | First off, they're a good name away from separating what you're
       | doing from how you're doing it.
       | 
       | Second, they're often part of a set. E.g. using a vector for a
       | stack, push(x) can be just a call to append(x), but pop() needs
       | to both read and delete the end of the vector. Push in isolation
       | looks like useless indirection, but push/pop as a pair are a
       | useful abstraction.
       | 
       | A consequence of adding these two points together is that, if you
       | have a good abstraction, and you have a good implementation that
       | maps well to the abstraction, it looks like useless indirection.
       | 
       | Another consequence is that those pass-through wrapper functions
       | tell you how I think the implementation maps to the domain logic.
       | In the presence of a bug, it helps you determine whether I got
       | the sequence of steps wrong, or got the implementation wrong for
       | one of the steps.
       | 
       | Ultimately, the two aren't completely independent --indirection
       | is one of the tools we have available to us to build abstractions
       | with. Yes, people misuse it, and abuse it, and we should be more
       | careful with it in general. But it's still a damned useful tool.
        
       | kristiandupont wrote:
       | Indirection serves a purpose as well. One that is related to, but
       | not the same as abstractions. When you add a layer of
       | indirection, you make it easier to, say, delete or change every
       | item X instead of iterating through everything.
       | 
       | Unnecessary or redundant levels of indirection are bad, just like
       | unnecessary or wrong abstractions are. But when applied
       | correctly, they are useful.
        
       | lifeisstillgood wrote:
       | I suggest there are three types of layer that one passes through
       | 
       | Abstraction - this thing of rare beauty
       | 
       | Decision - often confused for abstraction and wrapper, this is
       | best thought of as a case statement in a function. They are
       | wildly better in my opinion than lots of classes
       | 
       | Wrapper - either fluff like getters and setters or placeholders
       | for later decisions (acceptable) or weird classes and instances
       | that the language affords but tend to be confusing - what is
       | called indirection in the article
       | 
       | Tools, utils, libraries - these are I classify as handles /
       | affordances for other code to use - maybe they add layers but
       | they add a single way in to the nice abstraction above.
        
       | globular-toast wrote:
       | While I too like to marvel at the TCP/IP stack as an example of
       | abstraction done right, it would be unwise to think an
       | abstraction is only "good" if you get it right first time.
       | 
       | The real point of abstraction is to enable software that is
       | _adaptable_. If you are ever sure you can write a program the
       | first time and get it perfect then you don 't need to bother with
       | any of this thinking. We do that all the time when writing ad hoc
       | scripts to do particular tasks. They do their job and that's
       | that.
       | 
       | But if you ever think software will continue to be used then you
       | can almost guarantee that it will need to change at some point.
       | If it is just a tiny script it's no problem to write it again,
       | but that's not going to be acceptable for larger programs.
       | 
       | So this necessarily means that some layer or layers of your well-
       | architected application _will_ have to change. That does not mean
       | it was a bad abstraction.
       | 
       | Abstraction is not about hiding things, it's about building
       | higher levels of language. It enables you to work on individual
       | layers or components without breaking the rest of the system. It
       | very much should not be hiding things, because those things are
       | likely to need to change. The bits that really don't change much,
       | like TCP, are rarely written into application code.
        
       | psychoslave wrote:
       | The article seems to go with the premise that abstractions are
       | most often carelessly introduced when there is an obvious
       | alternative that is simpler and more performant.
       | 
       | Yes, abstractions have a cost that will accumulate as they are
       | layered.
       | 
       | But simple elegant solutions are not free. They are hard to come
       | with, so they often need large amount of dedication ahead of any
       | coding. And as long as we don't deliver anything, we have no clue
       | what actual requirements we miss in our assumptions.
       | 
       | The road to reach the nice simple solutions is more often than
       | not to go through some some clunky ugly solutions.
       | 
       | So rather than to conclude with "before running to abstraction
       | think wisely", I would rather recommend "run, and once you'll
       | have some idea of what was the uncharted territory like, think
       | about how to make it more practical for future walks."
        
         | globular-toast wrote:
         | But don't forget that the territory will change underneath your
         | feet. This is especially true if you write business software. A
         | tectonic shift in the business can make the assumptions you
         | made a year ago completely invalid.
         | 
         | This is complicated further by the fact that good software will
         | _drive_ the business. So you will always be creating new
         | problems because you 're driving the business into new areas
         | and capabilities that simply weren't possible before.
         | 
         | So this makes it doubly important to make sure your software
         | can change in small ways over time. It's not just trying to
         | navigate the moors at night with a torch, it's like trying to
         | navigate the desert at night with a torch. The sand will move
         | under your feet.
        
       | Voultapher wrote:
       | > That's the sign of a great abstraction. It allows us to operate
       | as if the underlying complexity simply doesn't exist.
       | 
       | While I generally agree with the sentiment that current day
       | software development is too indirection heavy, I'm not sure I
       | agree with that point. All abstractions are leaky and sure good
       | abstractions allow you to treat it like a black box, but at some
       | point you'd benefit from knowing how the sauce is made, and in
       | others you'll be stuck with some intractable problem if you lack
       | knowledge of the underlying layers.
        
         | ordu wrote:
         | It is funnily recursive...
         | 
         | I'll try to explain, but I'm not sure my English is good enough
         | for that task. But lets try.
         | 
         | When the author says "great abstraction" they mean "ideal
         | abstraction". You can see this for example in this quote: " The
         | less often you need to break the illusion, the better the
         | abstraction." They say even the phrase "all abstractions leak",
         | which is the main point of yours.
         | 
         | So, if they mean an "ideal abstraction", what does it mean?
         | What it means to be ideal? It means to be an imagined entity
         | with all sharp corners removed. The idea of "ideal" I believe
         | is an invention of Ancient Greeks, and all their art and
         | philosophy were built around them. Their geometry was an ideal
         | thing, that doesn't really exist anywhere except the brains of
         | a mathematician. Any ideal thing is not real by the definition.
         | 
         | Why to invent ideals? To simplify thinking about real entities
         | and talking about them. They allow us to ignore a lot of
         | complicating details to concentrate on the essence. So it is
         | like an abstraction, just not for programming but for thinking,
         | isn't it?
         | 
         | And now we come to the recursion. The author used abstraction
         | over abstraction to talk about abstractions, and you used
         | built-in deficiency of all abstractions (they are not real) to
         | attack the abstraction over abstraction.
         | 
         | Somehow it not as funny as I felt first, but still...
        
       | sixthDot wrote:
       | Another criticism would be the time spent to compile those
       | abstractions, even if in fine the "zero cost" goal _at runtime_
       | is reached.
        
       | scotty79 wrote:
       | Every problem can be solved by adding a layer of abstraction.
       | Except for the problem of having too many layers of abstraction.
        
       | Garlef wrote:
       | Wow... That was a lot of text without much depth to it.
       | 
       | [Edit] To make my criticism more precise: The text mostly
       | rephrases it's central point a few times and presents these
       | rephrasings as arguments.
        
         | mrcsd wrote:
         | Funnily enough, logical deductions or formal theorem proofs can
         | be seen as a set of transformative steps from the initial
         | premises to the conclusion, where no new information is added
         | in the process. Which makes the conclusion (at a stretch) a bit
         | like "just" rephrasing the initial premises.
        
           | scotty79 wrote:
           | Not really. Usually during the deduction you have some steps
           | that pull surprising knowledge from the rest of math to
           | support the reasoning or create and prove interesting lemmas.
           | 
           | If you can prove a theorem without any of that, that's a
           | little boring theorem to prove.
        
             | mrcsd wrote:
             | I agree with the spirit of your comment, but not the
             | literal fact of it. Sure, interesting proofs require
             | pulling out some interesting knowledge in the reasoning,
             | but notions like "surprising" or "interesting" are about
             | human subjectivity and don't really exist as a property of
             | a deduction. Surprising or interesting knowledge is not
             | somehow new knowledge that wasn't there before, it's just
             | that we didn't see it previously.
        
               | scotty79 wrote:
               | Sure, but it's humans that do the deduction. So
               | "surprising" and "interesting" still matters. Especially
               | when you are treating deduction as a parallel to a piece
               | of prose that one might reasonably hope to be surprising
               | and interesting not just repeated rephrasing of main
               | thesis.
        
       | freetonik wrote:
       | Shameless plug: a while ago I made a video explaining the idea of
       | abstraction in computer science, and it seems to be helpful for
       | beginners: https://youtu.be/_y-5nZAbgt4
        
       | mrcsd wrote:
       | Just thinking on my feet as to how I separate abstractions from
       | indirections and it seems to me that there's a relatively decent
       | rule of thumb to distinguish them: When layer A of code wraps
       | layer B, then there are a few cases:                   1) If A is
       | functionally identical to B, then A is a layer of indirection
       | 2) If A is functionally distinct from B, then A is likely an
       | abstraction         3) If A is functionally distinct from B, but
       | B must be considered when             handling A, then A is a
       | leaky abstraction.
       | 
       | The idea is that we try to identify layers of indirection by the
       | fact that they don't provide any functional "value".
        
       | nwmcsween wrote:
       | The goal of an abstraction should be to make reasoning about the
       | code easier, generally that means hiding complexity but that
       | shouldn't be the goal.
       | 
       | In my opinion a common issue in programming is premature
       | abstraction without understanding the interactions as a whole.
        
       | mrkeen wrote:
       | It starts with a strong point that abstraction is not
       | indirection, but then slips back into using the terms
       | interchangeably.
        
       | theGnuMe wrote:
       | Nice to see hacker news return to its roots.
        
       | havkom wrote:
       | I have seen tons of "abstractions" in recently created code bases
       | from "senior developers" which in actual fact is only titanic-
       | grade mess of complicated "indirection". Many people nowadays are
       | unfortunately not fit to work in software development.
        
         | mkoubaa wrote:
         | I disagree with nowadays. It has always been the case.
        
       | Gehinnn wrote:
       | A good abstraction shouldn't make its usage shorter, it should
       | make the proof that the usage is correct shorter.
       | 
       | This usually means the total amount of assumptions needed to
       | prove everything correct is decreased. (when the code is not
       | actually formally verified, think of "proof length" as mental
       | capacity needed to check that some unit of code behaves as
       | intended)
        
       | mitch-crn wrote:
       | This is the Unix philosophy: Write programs that do one thing and
       | do it well. Write programs to work together. Write programs to
       | handle text streams, because that is a universal interface. -Doug
       | McIlroy
        
       | alexvitkov wrote:
       | While I wholeheartedly agree with the premise, the article
       | doesn't really say anything other than "TCP good, your
       | abstraction bad, don't use abstraction".
        
         | bryancoxwell wrote:
         | That's not at all what the article says. It's not about
         | avoiding abstraction entirely, it's about implementing them
         | thoughtfully.
        
       | rauljara wrote:
       | I wish articles like this had more examples in them. In between
       | "this thin wrapper adds no value but a lot of complexity", and
       | "this thin wrapper clarified the interface and demonstrably saved
       | loads of work last time requirements changed" is an awful lot of
       | grey area and nuance.
       | 
       | I did like the advice that if you peak under the abstraction a
       | lot, it's probably a bad one, tho even this I feel could use some
       | nuance. I think if you need to change things in lots of places
       | that's a sign of a bad abstraction. If there is some tricky bit
       | of complexity with changing requirements, you might find yourself
       | "peeking under the hood" a lot. How could it be otherwise? But if
       | you find yourself only debugging the one piece of code that
       | handles the trickiness, and building up an isolated test for that
       | bit of code, well, that sounds like you built a wonderful
       | abstraction despite it being peaked at quite a bit.
        
         | mkoubaa wrote:
         | The way to tell whether an abstraction is good or bad is to
         | develop good taste. Engineers with good taste have intuition
         | about these things.
         | 
         | You are not going to acquire good taste from reading an
         | article.
        
           | baobabKoodaa wrote:
           | Maybe not, but you can still move the needle one way or
           | another based on reading an article. For those readers who
           | recognize themselves as erring on the side of adding too many
           | abstractions, they might move the needle a bit towards the
           | other side.
        
           | layer8 wrote:
           | Relying on mere "taste" is bad engineering. Engineers do need
           | experience to make good decisions, yes. But surely we are
           | able to come up with objective criteria of what makes a good
           | abstraction vs. a bad abstraction. There will be trade-offs,
           | as depending on context, some criteria will be more important
           | than other (opposing) criteria. These are sometimes called
           | "forces". Experience is what leads an engineer in assessing
           | and weighing the different present forces in the concrete
           | situation.
        
             | lokar wrote:
             | That's seems like it should be true, and it would be great
             | if it was.
             | 
             | But in my many years of experience working with Jr
             | engineers, I have found no substitute other then practice
             | guided by someone more Sr (who has good taste).
             | 
             | There are just too many different situations and edge
             | cases. Everything is situational. You can come up with
             | lists of factors to consider (better versions of this post
             | often have them), but no real firm rules.
        
               | layer8 wrote:
               | I wouldn't call that "taste". It's not a matter of taste
               | which solution is better. If different engineers disagree
               | about which solution to choose, then it's fundamentally a
               | different assessment of the relevant factors, and not
               | about taste. Or at least, it shouldn't be the latter.
        
               | lokar wrote:
               | Perhaps there is a better word. But there is a real skill
               | that you pretty much have to learn through experience and
               | mentorship.
        
               | layer8 wrote:
               | Yes, this is what I mentioned in my original comment
               | about experience being needed to weigh the trade-offs.
               | That doesn't mean that we can't very concretely speak
               | about the objective factors in play for any given
               | decision. We can objectively say that x, y, z are good
               | about this abstraction and a, b, c are bad, and then
               | discuss which might outweigh the other in the specific
               | context.
               | 
               | Needing experience to regularly make good decisions
               | doesn't mean that an article explaining the important
               | factors in deciding about an abstraction is useless.
        
               | kayo_20211030 wrote:
               | If we used the word "judgement", would that be a better
               | option? It seems that pretty much anyone can write code
               | (even AI), but ultimately in software development, we get
               | paid for judgement.
        
               | bc569a80a344f9c wrote:
               | I don't know. We could look for some other word to encode
               | "often sub-conscious though sometimes explicit heuristics
               | developed by long periods of experiencing the
               | consequences of specific trade-offs" but "taste" seems
               | like a pretty good one because it's quite intuitive.
               | 
               | There often - usually? - are more than one good solution
               | and more than one path to success, and I don't find
               | calling different good engineers making different choices
               | primarily because of their past experiences an egregious
               | misuse of language.
        
               | saltcured wrote:
               | I think you are going after something that is more an
               | element of craftsmanship than engineering, and I agree it
               | is a big part of real world software development. And,
               | not everyone practices it the same way! It's more of a
               | gestalt perception and thinking process, and that
               | instinctual aspect is colored by culture and aesthetics.
               | 
               | In my career, I've always felt uncomfortable with people
               | conflating software development with engineering. I think
               | software has other humans as the audience more so than
               | traditional engineered products. Partly this may be the
               | complexity of software systems, but partly it is how
               | software gets modified and reused. There isn't the same
               | distinction between the design and the product as in
               | other domains.
               | 
               | Other domains have instances of a design and often the
               | design is tweaked and customized for each instance for
               | larger, complex products. And, there is a limited service
               | life during which that instance undergoes maintenance,
               | possible refurbishing, etc. Software gets reused and
               | reformed in ways that would make traditional engineers
               | panic at all the uncertainties. E.g. they would rather
               | scrap and rebuild, and rely on specialists to figure out
               | how to safely recycle basic materials. They don't just
               | add more and more complexity to an old building, bridge,
               | airplane, etc.
        
             | marcosdumay wrote:
             | No kind of engineering ever gets into that "taste-
             | independent" level of formalization.
             | 
             | Yes, it should be this way. But it's not.
        
             | theptip wrote:
             | Folks like to claim that software is Engineering but it's
             | as much Craftsmanship. Hence, taste is in fact important.
             | 
             | In some areas you need more engineering but API design, for
             | example, is mostly taste and hardly any science.
        
         | mexicocitinluez wrote:
         | Amen.
         | 
         | Articles like this are a dime a dozen. Literally, there are
         | 1000s of articles that all say the exact same thing using way
         | too many words: "Bad abstractions are bad, good abstractions
         | are good".
        
         | epolanski wrote:
         | I second this, such posts are very generic, they are hard to
         | disagree with, but also to agree with empathically as there are
         | no clear examples of what is too much.
         | 
         | As someone who uses lots of layers and dependency injection I
         | would like to be poked on where is that too much abstraction
         | but I end up being no wiser.
        
         | CalChris wrote:
         | The article did start off giving TCP as a good abstraction but
         | then didn't follow up with examples of bad abstractions.
         | 
         | Dynamic typing is an example of an indirection masquerading as
         | an abstraction. You end up carrying around an object and
         | occasionally asking it whether it's an int64_t or a banana. You
         | maybe think your type luggage will take you on exotic vacations
         | when really in fact you take it on exotic vacations.
        
           | miki123211 wrote:
           | To me, it ties in with John Ousterhout's concept of "deep,
           | small interfaces"
           | 
           | TCP is a good abstraction because it's essentially 4
           | operations (connect, disconnect, send, receive), but there's
           | a lot going on inside to make these operations work. So are
           | TLS, filesystems, optimizing compilers and JITs, modern CPUs,
           | React (or rather the concept of "reactive UI" in general),
           | autograd and so on.
        
           | throwawaymaths wrote:
           | Isn't there a flip side to this? earlier today i saw someone
           | tweet that monads are indirection, not abstraction.
        
         | brunorb8 wrote:
         | I believe that the place to find detailed examples and deep
         | analysis is in books, not one-off web articles.
        
         | rmbyrro wrote:
         | try using LangChain and you'll get countless examples of bad
         | abstractions
         | 
         | started working with it this week for a new project
         | 
         | gosh, it's so painful and unintuitive... I find myself digging
         | deep into their code multiple times a day to understand how I'm
         | supposed to use their interfaces
        
         | jerf wrote:
         | "I wish articles like this had more examples in them."
         | 
         | There is a class of things that don't fit in blogs very well,
         | because any example that fits in a blog must be broken some
         | other way to fit into a blog, and then you just get a whole
         | bunch of comments about how the example isn't right because of
         | this and that and the other thing.
         | 
         | It's also a problem because the utility of an abstraction
         | depends on the context. Let me give an example. Let us suppose
         | you have some bespoke appliance and you need to provide the
         | ability for your customer to back things up off of it.
         | 
         | You can write a glorious backup framework capable of backing up
         | multiple different kinds of things. It enforces validity
         | checks, slots everything nicely into a .zip file, handles
         | streaming out the backup so you don't have to generate
         | everything on disk, has metadata for independent versions for
         | all the components and the ability to declare how to "upgrade"
         | old components (and maybe even downgrade them), support for
         | independent testing of each component, and has every other bell
         | and whistle you can think of. It's based on inheritance OO and
         | so you subclass a template class to fill out the individual bit
         | and it comes with a hierarchy pre-built for things like
         | "execute this program and take the output as backup" and an
         | entire branch for SQL stuff, and so on.
         | 
         | Is this a good abstraction?
         | 
         | To which the answer is, insufficient information.
         | 
         | If the appliance has two things to backup, like, a small SQL
         | database and a few dozen kilobytes of some other files, such
         | that the streaming is never useful because it never exceeds a
         | couple of megabytes, this is an atrocious backup abstraction.
         | If you have good reason to believe it's not likely to ever be
         | much more than that, just write straight-line code that says
         | what to do and does it. Jamming that into the aforementioned
         | abstraction is a terrible thing, turning straight code into a
         | maze of indirection and implicit resolution and a whole bunch
         | of code that nobody is going to want to learn about or touch.
         | 
         | On the other hand, if you've got a dozen things to backup, and
         | every few months another one is added, sometimes one is
         | removed, you have meaningful version revs on the components,
         | you're backing up a quantity of data that perhaps isn't
         | practical to have entirely in memory or entirely on disk before
         | shipping it out, if you're using all that capability... then
         | it's a fantastic abstraction. Technically, it's still a lot of
         | indirection and implicit resolution, but now, compared to
         | "straight line" code that tries to do all of this in a
         | hypothetical big pile of spaghetti, with redundancies,
         | idiosyncracies of various implementations, etc., it's a huge
         | net gain.
         | 
         | I don't know that there's a lot of abstractions in the world
         | that are simply _bad_. Yeah, some, because not everything is
         | good. But I think they are greatly outnumbered by places where
         | people use rather powerful, massive abstractions meant to do
         | dozens or hundreds of things, for two things. Or one thing. Or
         | in the worst case, for no things at all, simply because it 's
         | "best practices" to put this particular abstraction in, or it
         | came with the skeleton and was never removed, or something.
        
       | PaulHoule wrote:
       | Without more details his position rubs me the wrong way.
       | 
       | As somebody who has done a huge amount of "fix this bug" and "add
       | this feature" on existing code bases I think excessive use of cut
       | and paste is the worst problem in the industry. Cut-and-paste is
       | the devil's own "design pattern" as it is a practice that gets
       | repeated throughout a code base to solve various problems.
       | 
       | When it comes to bugs repetition means a bug might have 13 copies
       | throughout the code and you could easily get the ticket sent back
       | 2 or 3 times because you didn't find all the copies at first.
       | 
       | Repetition (together with poorly chosen abstractions) also causes
       | features that should add in complexity to multiply, as if I have
       | 3 versions of a function and now something can vary 5 ways I now
       | have 15 functions. In a good design you might pass one of 8
       | functions to a 9th function. Repeat this a few times and one guy
       | has 98 functions and the other guy would have had 13200 if he'd
       | been able to get that far.
       | 
       | Granted the speed demon won't like all that function calling,
       | right now I am thinking about writing a big switch statement for
       | a CPU emulator, I get it, for you there is code generation.
       | 
       | It is also healthy to have "fear of framework planets", a
       | horrible example is react-router which has gotten up to
       | incompatible version 7 because (i) it's the kind of thing you can
       | write in an afternoon (it would take more time to write good
       | documentation but... take a look at that documentation) and (ii)
       | the authors never liked any of the frameworks they created. More
       | than once I have dug into a busted app written by a fresher where
       | there was a copy of react-router and there were some use's from
       | it in use but they had bypassed react-router and parsed
       | document.location directly to figure out what to display. The
       | very existence of a bad framework creates a kind of helplessness.
       | 
       | Those folks will say various versions of react-router support
       | React Native, SSR, etc. We don't use any of those where I work, I
       | don't care.
       | 
       | It is a very good prop bet that you can dramatically speed up so-
       | and-so's program by switching from AoS to SoA.
       | 
       | https://en.wikipedia.org/wiki/AoS_and_SoA
       | 
       | (If it's a Java program, you eliminate the overhead of N objects
       | to start with)
       | 
       | but it's tricky to implement arbitrary algorithms, my mental
       | model to do it is to build programs out of relational operators
       | (even in my head or on paper) SQL is one of the greatest
       | abstractions of all time as I can write a SQL query and have it
       | be run AoS or SoA or some hybrid as well as take advantage of
       | SIMD, SMT, GPU and MP parallelism. 10 years ago I would have said
       | I could have beat any SQL engine with hand-optimized code, today
       | products like
       | 
       | https://duckdb.org/
       | 
       | would make that harder.
        
       | sltr wrote:
       | When discussing the definition of abstraction, Koppel's article
       | "Abstraction: Not What You Think It Is" offers a helpful framing
       | and disambiguation.
       | 
       | https://www.pathsensitive.com/2022/03/abstraction-not-what-y...
        
       | herdcall wrote:
       | To me, the value of abstraction is more about making the code
       | flexible than hiding complexity.
        
         | vacuity wrote:
         | Perhaps, but isn't it flexible _because_ complexity was hidden?
        
       | marginalia_nu wrote:
       | While I think it's good the pendulum is swinging toward a more
       | restrictive approach to abstractions, we've (and I've) certainly
       | been leaning a bit too much toward just solving every problems by
       | adding a layer of indirection around it, and such onion-layered
       | designs tend to (as the metaphor implies) cause a lot of tears
       | when you cut through them. That said, it's not like abstraction
       | itself is bad.
       | 
       | A big part of the problem is arguably that IDEs make code
       | navigation easier, which has us adding all these indirections and
       | discover only when it's too late what a horrible maze we've
       | built. Being more judicious about adding indirection really does
       | help force better designs.
        
       | johnfn wrote:
       | I found this to be a pretty poor article. The article lacks
       | concrete examples and speaks in generalities to explain its core
       | thesis. But without specificity, a reader can nod along, sure in
       | the knowledge that they already are wise and follow this advice
       | and it's _everyone else_ who 's out there mucking up code bases
       | with layers upon layers of garbage. I mean,
       | 
       | > The next time you reach for an abstraction, ask yourself: Is
       | this truly simplifying the system? Or is it just another layer of
       | indirection?
       | 
       | Is anyone reading this truly going to alter their behavior? If I
       | could recognize that my abstraction was "just another layer of
       | indirection" as simple as that, obviously I wouldn't have added
       | it in the first place!
        
         | ahuth wrote:
         | I don't know. Sure, examples could be nice. But an article can
         | be imperfect and still be interesting or useful. Also, it's
         | easy to say come up with examples, but I find it hard
         | sometimes.
         | 
         | In any case, I find it a nice article. Will it change how I
         | write code? Maybe, maybe not. But it will change how I review
         | code and talk about abstraction.
        
       | toolslive wrote:
       | I don't consider TCP an abstraction at all. The abstraction is
       | the unix API over it, and then again, the
       | 
       | > ssize_t send (int socket, const void *buffer, size_t size, int
       | flags)
       | 
       | is not a nice one. When was the last time you had the data in a
       | buffer, wanted to send it over to the peer at the other side, but
       | didn't mind that it's not sent in it's entirety ? So you have to
       | write a loop over it. Also, is the call blocking or not ? (well,
       | you'll have to read the code that created it to know, so that's
       | no fun neither).
       | 
       | However, it does prove the point the author is trying to make:
       | good abstractions are hard to find!
       | 
       | Anyway, I tried to think of a better example of a good
       | abstraction and found the "Sequence" that's available in plenty
       | of programming languages. You don't need to now what the exact
       | implementation is (is it a list, a tree, don't care!) to be able
       | to use it. Other example I found were Monoid and Monad, but
       | that's tied to the functional paradigm so you lose most of the
       | audience.
        
       | phtrivier wrote:
       | Let's not forget about a particularly frustrating kind of "level
       | of abstraction": the bespoke interface to a part of the code that
       | has side effect, and that has exactly two implementation : one in
       | the production code, and one in the tests.
       | 
       | If I were to create a language tomorrow, that's the one aspect
       | where I would try something that I have not yet found elsewhere :
       | can you make it so that you can "plug" test doubles only for
       | test, but keep the production path completely devoid of
       | indirection or late binding.
       | 
       | (I'm curious if you know of a language that already does that. I
       | suppose you can hack something in C with #ifdef, of course...)
        
       ___________________________________________________________________
       (page generated 2024-12-28 23:00 UTC)