[HN Gopher] CUPID - the back story
       ___________________________________________________________________
        
       CUPID - the back story
        
       Author : blowski
       Score  : 289 points
       Date   : 2021-03-21 15:17 UTC (7 hours ago)
        
 (HTM) web link (dannorth.net)
 (TXT) w3m dump (dannorth.net)
        
       | sbussard wrote:
       | The argument against the Liskov Substitution principle was the
       | most interesting to me. It uncovers the principle that makes the
       | world go round: composition. While it's easy to decompose code
       | into components of single responsibility, I think the rules of
       | composition are not clearly understood. Category Theory has
       | already told us that composition depends on context (what the
       | thing is used for). Yet some problems are intrinsically coupled,
       | having relationships across layers of abstraction. In those cases
       | the developer should broaden the definition of what "one thing"
       | is. A process emerges for writing such code: gather deep
       | understanding of the domain > develop an accurate mental model
       | for what is to be accomplished > make wise choices on how define
       | categories of code. Overall not wrong, just insufficient.
        
       | sholladay wrote:
       | At first, I thought this was going to be about the Solid Project
       | (which is sometimes stylized as SOLID) by Tim Berners-Lee. It's
       | not. That would have been fun.
       | 
       | Regarding SOLID principles being outdated, I disagree. The high
       | level concepts of SOLID are good and just as relevant as ever.
       | While the author proposes that code is now less risky to change,
       | I would argue that the asynchronous, distributed systems that we
       | often work with today are significantly more complex and error
       | prone. The world also got hooked on dynamic languages for the
       | past couple of decades, although it feels like that shift is
       | slowly reversing. Software bugs have become a routine part of
       | daily life, partly because there's more software but also because
       | we build that software more haphazardly to achieve deadlines for
       | ever expanding sets of features. Today, a large number of
       | developers lack proper education or training and have relatively
       | little experience. We work in teams where there's a lot going on
       | besides writing code and there's more distraction around us than
       | ever before. To me, there's no shortage of reasons to worry about
       | changing a line of code. I spend a lot of my time trying to
       | understand and reduce that risk so that I and everyone around me
       | can be more productive in the long run. The SOLID principles are
       | a useful guide in that effort.
       | 
       | That said, you may want to update your interpretation of SOLID
       | within the context of your latest coding practices. For me, that
       | meant applying SOLID to functional programming. At first this
       | seems odd, since much of the language around SOLID implies the
       | use of classes and object-oriented programming. But it turns out
       | there is a lot of overlap between the philosophies of SOLID and
       | functional programming. [1]
       | 
       | 1: https://dev.to/patferraggi/do-the-solid-principles-apply-
       | to-...
        
       | surfsvammel wrote:
       | To me Single Responsibility means that the code should do one
       | thing and one thing only, on the abstraction level which it
       | exists.
       | 
       | The example given by OP is about ETL. Is that one thing or more
       | than one thing? It depends on the abstraction level. If you are
       | talking about a data access object, sure let it do all those
       | things because when considered on that abstraction layer it does
       | one thing, access data.
       | 
       | But if you are looking at individual functions or methods, that
       | is one abstraction layer further down, then S in SOLID says you
       | should not mix extraction and transformation, for example.
       | 
       | It all depends on context and SOLID are principles not law.
        
       | einpoklum wrote:
       | "single responsibility" is not reducible/replaceable with "code
       | that 'Fits In My Head'".
       | 
       | Our head may very well fit two pieces of code which are disparate
       | and which would be use to separate into distinct functions.
       | 
       | ----
       | 
       | "Open-Closed Principle... was sage advice in an age where ... we
       | hadn't figured out refactoring yet ..."
       | 
       | Well, as far as I'm concerned, we haven't "figured out
       | refactoring" yet. I'm currently working with a group of people
       | 75% of whose' work is just gradually and slowly refactoring an
       | existing codebase.
       | 
       | "Nowadays, the equivalent advice if you need code to do something
       | else is: Change the code to make it do something else! It sounds
       | trite, but we think of code as malleable now like clay"
       | 
       | That may be true when you're writing code for your own use, or
       | the use of a very small group of people (the large "user base"
       | doesn't work with the code, they interact with what the code
       | does). When you're writing a library, or code with which near-
       | strangers need to work, you can't just willy-nilly change things.
       | Moreover, you must strive to make your code good enough so that
       | it doesn't need to be changed all the time; an ever-changing
       | codebase is difficult to rely on (unless somebody takes a
       | snapshot, and is back in the case of never-changing code).
       | 
       | ---
       | 
       | ... but I do appreciate some of the points made. Defining what
       | "one thing" means is definitely not trivial and obvious, for
       | example.
        
         | burade wrote:
         | >When you're writing a library
         | 
         | 99% of developers are working in boring business software with
         | 2-3 other developers.
         | 
         | 99% of codebases will be touched by maybe a dozen developers in
         | its entire lifetime.
        
           | The_rationalist wrote:
           | Did you make up those numbers out of your head? When you
           | don't refer to a specific (Meta)analysis you can still make
           | up numbers from guessing but if you do you should give
           | intervals e.g replace 99% by most
        
       | krona wrote:
       | The author presents a series of strawman arguments to debunk
       | SOLID, and then suggests that instead we should make software as
       | complex as possible but no more complex than the coders own
       | comprehension. In my experience this commonly how
       | incomprehensible codebases evolve; the code is comprehensible
       | right up to the point that it isn't, and by that point it's too
       | late to change anything.
        
         | legulere wrote:
         | That's the point where you need refactoring. The basic problem
         | is that you cannot look into the future and clearly see what
         | exactly you are going to need (if you are lucky enough to
         | implement something along a specification, you should us that
         | knowledge though!).
         | 
         | As long as you manage to keep artificial complicatedness out of
         | your code, you will always have the complexity of the problem
         | mirrored in your code. A common problem of ideas about object
         | oriented programming like SOLID or Clean Code is that they have
         | a focus on classes. If you keep your classes very simple, you
         | will instead end up with a complex architecture, where you
         | might have zero responsibility layers or functions that just
         | pass on the call further to a layer down.
        
         | rootsofallevil wrote:
         | that's an interesting take, what would you consider strawman
         | arguments in the article?
        
           | tanseydavid wrote:
           | His entire screed on single responsibility principle spins
           | around on semantics.
        
           | sorokod wrote:
           | Not the person you asked but, I would say that the
           | expectation that SOLID provides or should provide,
           | unambiguous guidance
        
           | refactor_master wrote:
           | For example, the open-close principle. The author blames this
           | advice on tooling of the 90s and proposes instead "Change the
           | code to make it do something else".
           | 
           | This has nothing to do with tooling, but the fact that
           | pulling the rug under an established code base could have
           | very unintended effects, compared to simply adding and
           | extending functionality without touching what is already
           | there.
           | 
           | By doing as the author suggests you'll end up with either 500
           | broken tests or 5000 compiler errors in the best case, or in
           | the worst case an effectively instantly legacied code base
           | where you can't trust anything to do what it says.
           | 
           | I once had to change an entire codebase's usage of ints to
           | uuids, which took roughly 2 whole days of fixing types and
           | tests, even though logically it was almost equivalent.
           | Imagine changing anything and everything to "make it do
           | something else".
        
             | garethrowlands wrote:
             | It has a fair bit to do with tooling. For example, C++
             | suffers from the fragile base class problem and some
             | changes can cause long compile times. Nowadays, we have
             | tests and deployment pipelines that are explicitly designed
             | to let us make and deploy changes safely.
             | 
             | Honestly, if you cannot change your code, you have a
             | problem.
        
               | chillacy wrote:
               | The OCP is imo poorly named, but it has far bigger
               | implications than the post acknowledges. For one, it
               | implies the concept of abstraction layers. In particular,
               | base libraries should provide abstractions at the level
               | of the library. In this way it's able to achieve being
               | "closed for modification but open for extension".
               | 
               | https://drive.google.com/file/d/0BwhCYaYDn8EgN2M5MTkwM2Et
               | NWF...
               | 
               | Flipping it around, if base libraries were to be always
               | open for modification instead of extension, then instead
               | of writing your feature logic in your own codebase, you
               | might be tempted to submit a pull request to React.js to
               | add your feature logic. That sounds ridiculous but that's
               | the equivalent of what I see a lot of new engineers do
               | when they try to fit the feature logic they need to
               | implement anywhere it makes sense, often in some shared
               | library in the same codebase.
        
             | coldtea wrote:
             | > _This has nothing to do with tooling, but the fact that
             | pulling the rug under an established code base could have
             | very unintended effects, compared to simply adding and
             | extending functionality without touching what is already
             | there._
             | 
             | That's still a matter of tooling. With type-checking,
             | static analysics, and test suites, changing code doesn't
             | have "very unintended effects".
             | 
             | Back in the day, without those, things were much more
             | opaque.
        
             | geofft wrote:
             | What's the alternative here? If you had to change a
             | codebase's usage of ints to uuids, should the original
             | author have used dependency inversion and required an
             | IdentifierFactory that was ints at the time so you could
             | just swap out the implementation? And if they did - why
             | wouldn't they have just used UUIDs in the first place?
             | You're betting on the fact that the original author
             | anticipated a particular avenue of further change, but
             | _also_ made the wrong decision for their initial
             | implementation, which seems like the wrong bet. If they
             | made the wrong decision, they almost certainly didn 't
             | anticipate the need for another one, either.
             | 
             | And how long would it have taken for the original author to
             | use an IdentifierFactory instead of ints and write
             | meaningful tests for it? Less than two days?
        
               | jdlshore wrote:
               | Changing ints to UUIDs is a classic example of the
               | Primitive Obsession smell, and the solution is to wrap
               | primitives in a type representing their semantic meaning,
               | such as "ID," or "PrimaryKey," not to use a factory. That
               | way, when you need to change the underlying type from int
               | to UUID, you only need to do it in one place.
        
               | razorfen wrote:
               | In the uuid case, the person had no choice. Remember,
               | these are principles, not laws, and at some point your
               | system is making concrete choices. Choosing UUIDs isn't
               | necessarily a OO design problem. He was just highlighting
               | how expensive it can be if you require changes to your
               | fundamental classes to extend or change behavior. In the
               | identifier type case, it's rare that folks abstract this
               | stuff away. Though: I do know a LOT of systems that use
               | synthetic identifiers for this exact purpose, as larger
               | enterprises tend to deal with many more different
               | identifier types from different integrations, from a DB
               | type that can't hold new identifiers, because IDs need to
               | be sharper/distributed etc. So yeah, it's a principal,
               | and one should choose if it's worth the upfront cost for
               | its benefits.
               | 
               | OCP though more commonly refers to: 1. Building small,
               | universally useful abstractions first 2. Extending
               | behavior of that abstraction or system by writing new
               | code rather than changing published code directly.
               | 
               | This is trivial when you have a few patterns under your
               | belt. Template factories, builders, strategies, commands.
               | I mean, while it's not the best idea in most cases, even
               | just inheriting a parent class and giving a new concrete
               | new behavior is still better than changing something
               | fundamental to the system.
               | 
               | Like has been said 999 times in this thread, software
               | isn't black and white. You have to make choices about
               | where you go concrete and where you abstract, and gauge
               | that against risk factors. A somewhat complex class you
               | expect to go away in a couple months? Make it a god class
               | that anyone who wants to can scan through. A fundamental
               | class that will be used by hundreds of developers and
               | underpin most operations in a production system where 5
               | minutes of downtime costs tens of thousands of dollars?
               | It's worth the upfront cost to build with these
               | standards.
        
         | coldtea wrote:
         | > _The author presents a series of strawman arguments to debunk
         | SOLID_
         | 
         | Care to elaborate as to why those are strawmans?
         | 
         | Without that, this is a no-argument, which is probably worse
         | than a strawman.
         | 
         | > _and then suggests that instead we should make software as
         | complex as possible but no more complex than the coders own
         | comprehension_
         | 
         | The combo (go and make "software as complex as possible but no
         | more complex than the coders own comprehension") is suggested
         | nowhere in the post.
         | 
         | The second part of the combo (make software "no more complex
         | than the coders own comprehension") is indeed said, and is very
         | sensible advice.
         | 
         | It is, however, combined with the inverse advice of "make it as
         | complex as possible" which you claim the author combined it
         | with: with the advice to keep it simple.
        
         | geofft wrote:
         | In my opinion, that's in fact exactly how you should be
         | developing your software.
         | 
         | Code is cheap to write, and mostly debt. A software product - a
         | working system that meets some particular need - is not. The
         | distinction is that building a software product is much more
         | than banging out code; it's the experience of figuring out what
         | exactly that need is (gathering requirements, getting a system
         | out for users to test, getting real-world feedback). Sometimes
         | you capture that in documentation, test cases, ADRs, comments,
         | commit messages, etc. Sometimes it's in your head, which is
         | okay as long as you're still there. (Of course, if it's in your
         | head and you leave, then the next set of programmers will be
         | scared to change the system until they redo the work of
         | figuring out what the _software_ is, despite the code being in
         | front of them.)
         | 
         | If you have that understanding about what the software does,
         | ideally in the form of automated test cases, you can rewrite
         | the code. So you may as well bang out the code in a way that
         | gets you a working system and remains comprehensible. Once
         | you're making enough changes to it that you're worried about it
         | getting incomprehensible, proceed to rewrite it. Probably the
         | world has changed in many ways sine you wrote it - maybe you
         | can run the system for a lot faster and cheaper with containers
         | in the cloud talking to a SaaS database than with your
         | expensive IBM mainframe talking to DB2. Or perhaps the
         | requirements are changing significantly, and starting with the
         | old code doesn't give you much of an advantage.
         | 
         | Trying to keep a codebase comprehensible over the long term
         | makes it, if you'll excuse the pun, solidify - it becomes
         | increasingly hard to make changes that weren't anticipated by
         | the original design, and it also requires more and more effort
         | to just do _anything_. You might be able to swap out the
         | relational database for another one, but you probably can 't
         | switch to a design where you're using a key-value store with
         | totally different performance characteristics. And even if you
         | do just want to change the database, you have to figure out how
         | ten classes get dependency-injected into each other before you
         | can start coding.
         | 
         | All the time you spent making the code "future-proof" so it
         | remains forever comprehensible could have been spent simply not
         | doing that, delivering business value, and writing new code as
         | the need arises.
        
         | jdlshore wrote:
         | > he suggests we should make code as complex as possible
         | 
         | I read no such thing. (Speaking of straw men...) instead, I
         | read him saying that the alternative to SOLID was "Write simple
         | code."
         | 
         | I think there's room to criticize this essay, but that's a
         | bizarre one to lead with.
        
           | jancsika wrote:
           | > instead, I read him saying that the alternative to SOLID
           | was "Write simple code."
           | 
           | The part you missed is in the same sentence as the three
           | words you quoted.
           | 
           | "Instead I suggested to write simple code using the heuristic
           | that it 'Fits In My Head'."
           | 
           | The obvious criticism here is that now _any_ developer who
           | wants to defend their code will simply claim a given
           | spaghetti incident easily fit in _their_ head. The author
           | even seems to acknowledge this line of attack in the next
           | paragraph:
           | 
           | > You might ask "Whose head?" For the purpose of the
           | heuristic I assume the owner of the head can read and write
           | idiomatic code in whichever languages are in play, and that
           | they are familiar with the problem domain. If they need more
           | esoteric knowledge than that, for instance knowing which of
           | the many undocumented internal systems we need to integrate
           | with to get any work done, then that should be made explicit
           | in the code so that it will fit in their head.
           | 
           | But that just protects against spaghetti made from esoteric
           | know of internals. For example, in C a "big head" would still
           | be able to justify using global variables (after all, that's
           | idiomatic C), rolling their own buggy threading approach, and
           | deeply nested side-effect based programming.
           | 
           | I'd much prefer pointing big heads to the rule of "do one
           | thing" than to _ever_ read a response of the category,
           | "Well, it fits in _my_ head. "
        
             | lupire wrote:
             | Not defending the article at all, but "whose head" should
             | be at least a code reviewer's head, ideally two.
        
               | alfonsodev wrote:
               | I believe that there are people that can handle more
               | abstractions in their working memory, maybe up to 6 or 7
               | layers of abstraction, and there are people that can't go
               | further than 3, before feeling lost.
               | 
               | The "bigger heads" types will use SOLID or any other
               | argument to justify a higher number of abstractions.
               | While the other type will do the same to justify a
               | lesser.
               | 
               | At the end of the day, there is no right solution, but
               | what is so called "cultures" that group people with
               | similar taste and cognitive fingerprint, and provide best
               | practices.
        
               | Pxtl wrote:
               | Yup. As a PR reviewer, my gold standard is "I understand
               | 100% of what's going on in this change, and I'm confident
               | it does what it says it does". All the rules and
               | standards and stuff we put in place is basically in
               | support of that goal.
               | 
               | If I can't understand it, neither can the next guy.
        
             | refactor_master wrote:
             | Fits in my head and works on my computer.
             | 
             |  _slaps on hood_
        
             | gregmac wrote:
             | > The obvious criticism here is that now any developer who
             | wants to defend their code will simply claim a given
             | spaghetti incident easily fit in their head.
             | 
             | The thing is: that's just a bad defense. If the people who
             | are going to be to maintaining it and working with it are
             | saying it's bad code, it's bad. Even if this is the only
             | developer who is going to maintain it for now (which itself
             | is always a terrible idea), eventually someone else will
             | take over, and the closest thing you have to that "someone
             | else" is the rest of the current team.
             | 
             | I'm having a hard time imaging someone seriously making
             | this argument, who is not also:
             | 
             | * Trying to establish themselves as the sole possible
             | maintainer, so either the company can't fire them or is
             | completely screwed if they ever leave, or:
             | 
             | * Is a saying the rest of their team is just too stupid to
             | work with
             | 
             | In either case, this person is a massive liability, and the
             | longer they are employed the more damage they'll do.
        
         | intricatedetail wrote:
         | Writing complex and convoluted code is a strategy used by some
         | developers who bill by day or an hour, so then they have to
         | spend extra time to "understand" or "refactor". I've seen this
         | so many times. One even got visibly angry when I politely
         | pointed out his solution could be vastly simplified or even
         | skipped as the client was considering different options.
        
       | bwilliams wrote:
       | I read the article and didn't really feel that they disagreed
       | with, or debunked any of the principles. It reads like they
       | formed their own understanding of each principle and maybe
       | disagreed with how they were taught, or how the principles are
       | sometimes presented.
       | 
       | This change in outlook of existing thoughts/ideas is how many
       | crafts grow, such as martial arts, painting, philosophy, etc
       | (instead of stagnating). Sometimes we need to frame things in a
       | more modern manner, and sometimes we need to discard them
       | completely. In this case, I think re-framing the concepts is
       | helpful, and I found it to be an interesting point of view. I
       | agreed with a good amount of it, but I don't think we need to
       | discard SOLID principles just yet.
        
         | pydry wrote:
         | This alludes to one of my main beefs with SOLID - the lack of a
         | clear definition.
         | 
         | What counts as a "responsibility", for instance. Where I see
         | one responsibility some people see two and vice versa.
        
           | aranchelk wrote:
           | Pauli, in one of the sickest ever physics burns, said of his
           | colleague's work that it was "not even wrong."
        
           | barbazoo wrote:
           | I agree. In a way that's one of the strengths of SOLID in my
           | opinion. All places I've worked at had slightly different
           | versions of what e.g. SRP meant. And that's ok. It's a way to
           | make sure your team writes code in a way their teammates
           | would expect. Whether that's objectively good code or not
           | doesn't matter as much to me.
        
           | SideburnsOfDoom wrote:
           | > What counts as a "responsibility", for instance. Where I
           | see one responsibility some people see two and vice versa.
           | 
           | The Single Responsibility Principle is not a rigorously
           | defined Scientific law, where the "responsibility count" can
           | be exactly measured and minimised.
           | 
           | It is a subjective design guideline, with elements of
           | experience, of context and of "I know it when I see it".
           | 
           | This does _not_ make it useless. It is still very useful
           | indeed. But you do have to understand that disagreements over
           | it are not always about "who is objectively right" but "which
           | style makes the most sense in context".
        
             | raverbashing wrote:
             | Here's the problem:
             | 
             | Some developers will say a (data structure) Controller is a
             | class obeying the SRP
             | 
             | Some others will say the class can manage itself and not
             | need a controller, so M and C can be one thing.
             | 
             | Some other will argue that it's better to make 2
             | controllers, one for complicated thing 1 and another for
             | complicated thing 2, all based on the same Model.
        
               | d110af5ccf wrote:
               | That's not a problem, it just means the set of design
               | tradeoffs to consider when solving the problem is
               | nontrivial.
               | 
               | The principle draws focus to certain (arguably important)
               | design aspects. Multiple possible approaches are
               | identified. Various concerns are raised in response. A
               | good solution needs to balance these concerns against one
               | another in context.
               | 
               | The principle is just one of many cognitive tools to
               | employ when thinking about the problem.
        
             | pydry wrote:
             | _shrug_ I think every time I 've ever seen a disagreement
             | about code quality it's boiled down to both developers
             | thinking "I know it when I see it" about their separate
             | approach.
             | 
             | If a set of principles lets them both think they're both
             | correct and the other one is wrong, what exactly is the
             | point of those principles?
             | 
             | This isn't just a coding thing. It's also, say, why society
             | follows a body of laws rather than something like the 10
             | commandments.
        
               | ehnto wrote:
               | There are so many instances in code where two options are
               | just as good as eachother by some negligible margin. The
               | actual problem that needs solving is figuring out how to
               | compromise and collaborate.
               | 
               | That's why, if I am on a team project, I much prefer
               | working in opinionated frameworks with strong idioms. It
               | actually doesn't matter if I think I could do it better,
               | the framework has chosen a different way and that is
               | fine. We all have to do it that way, and we can all
               | compromise and collaborate. No one bickers about best,
               | and we can get real work done.
               | 
               | Different when it is a project of my own, but if it
               | requires teamwork you need a framework of collaboration
               | as much as a framework of code.
        
               | pydry wrote:
               | >There are so many instances in code where two options
               | are just as good as eachother by some negligible margin.
               | 
               | This is my other beef with SOLID: no trade offs.
               | 
               | I'd even extend your point to say that there are many
               | times when code is not ideal but fixing it isn't
               | worthwhile.
               | 
               | It would be nice to have a set of principles which
               | recognized costs rather than promoting a vague, idealized
               | standard for developers to fight over.
        
               | d110af5ccf wrote:
               | > lets them both think they're both correct and the other
               | one is wrong, what exactly is the point of those
               | principles
               | 
               | Guidelines can be useful even when subjective. Vitriolic
               | disputes over whose (subjective) view is "correct" is an
               | example of toxic behavior, not a problem with the
               | guidelines being used to justify such behavior.
               | 
               | What would you think of someone who loudly insisted
               | (without humor) that putting pineapple on pizza was wrong
               | as a matter of principle? Is such expression in any way
               | useful?
        
           | bwilliams wrote:
           | I don't disagree, and that lack of common interpretation can
           | be both good and bad. Good because it lets you apply your own
           | understanding and experiences to it, and bad because it
           | introduces the potential for conflict when two people have
           | different understandings.
        
           | bick_nyers wrote:
           | I think that is a feature, not a bug. I think it makes sense
           | in different contexts for "responsibility" to be abstracted
           | differently. The two extremes are lots of files and functions
           | versus fewer files and functions, and the optimal balance to
           | strike is probably based on whether it is important for
           | people to focus on the modules, or the arrangement of those
           | modules. For high-performance C++ libraries with a good
           | design/architect, it could make sense to split up to a lot of
           | files/functions, so that each function can be iterated upon
           | and improved. For a less-performance sensitive Java library
           | where understanding and usage is most important, you would
           | want less files/functions such that the development focus is
           | more on the high level ideas, the arrangement of the parts
           | (or refactoring).
           | 
           | With any paradigm, there is often ambiguity with certain
           | elements, because those elements should be dynamic. What
           | SOLID aims to do is say that these main points are not
           | something you should dedicate brain cycles towards, as they
           | are best spent elsewhere in the design.
        
             | pydry wrote:
             | >With any paradigm, there is often ambiguity with certain
             | elements, because those elements should be dynamic.
             | 
             | It's because, unless you're careful, human language is
             | insufficiently precise by its nature for many domains.
             | 
             | This is why mathematicians communicate with mathematical
             | notation, for instance. It's why lawyers use special lawyer
             | only terms (or why interpretation of the law is an
             | explicitly separated function).
             | 
             | With SOLID the lack of precision renders the whole exercise
             | rather pointless.
             | 
             | You're supposed to be able to use these principles as
             | something you can agree upon with other developers so that
             | you have a shared understanding of what constitutes "good"
             | code.
             | 
             | However, it doesn't happen. Instead we argue over what
             | constitutes a single responsibility.
             | 
             | SOLID isn't the only example of this. "Agile" is actually
             | far worse.
        
               | bick_nyers wrote:
               | Yes that is definitely true, human language is very
               | limited. Although, a paradigm is only as strong as it's
               | adoption, and by reducing the barrier to entry (using
               | common language as opposed to overly precise notation),
               | the principles can spread faster and with less friction
               | say inside of an organization. Nuance is always more
               | optimal, for sure. How many people can rattle off the
               | Fundamental Theorem of Calculus? I would think that most
               | people remember the idea behind it, but not the precise
               | mathematical definition. In fact, the definition learned
               | in undergraduate calculus is not 100%, as early
               | undergraduates are not expected to have the rigor and
               | experience to handle real analysis.
        
               | SideburnsOfDoom wrote:
               | > With SOLID the lack of precision renders the whole
               | exercise rather pointless.
               | 
               | If your assumption is that design guidelines can have
               | mathematical precision, then you're going to be
               | perpetually disappointed.
               | 
               | > Instead we argue over what constitutes a single
               | responsibility.
               | 
               | I'm sorry that you can't reach consensus. but IMHO, the
               | false idea that there is a single, mathematically correct
               | answer (and if people differ, therefor someone must be be
               | Wrong, capital W) is often part of the problem, of what
               | stops an agreement on how to move forward pragmatically
               | being reached.
        
               | pydry wrote:
               | Being vague isn't the only thing wrong with SOLID. The
               | idea that there is "one true way" is partly what bugs me
               | about it (and uncle bob in general).
               | 
               | I don't think that there is a set of design guidelines
               | that can be applied with mathematical precision, but
               | there are a set of code "costs" (like cyclomatic
               | complexity) which can be measured with mathematical
               | precision.
               | 
               | That is to say, all other things being equal, if a pull
               | request non-negligibly reduced cyclomatic complexity I'd
               | be happy to claim that it increased code quality. I don't
               | believe that this idea is false.
        
               | SideburnsOfDoom wrote:
               | > there are a set of code "costs" (like cyclomatic
               | complexity) which can be measured with mathematical
               | precision.
               | 
               | "Responsibility" is IMHO about intent and meaning, which
               | as a human concept is not reducible to measurements of
               | this kind. Cyclomatic complexity is all fine and well,
               | but it cannot tell you if an intent is being met or not.
               | It is silent as to meaning. It's relevant only if you can
               | compare two pieces of code that do the same thing; it's
               | not talking about what that thing should be.
        
               | SideburnsOfDoom wrote:
               | > The idea that there is "one true way" is partly what
               | bugs me about it (and uncle bob in general).
               | 
               | I tend to agree with that, and the original article in
               | general. Mr Martin's approach helped originally, but the
               | dogma now can and should be moved on from.
        
       | geofft wrote:
       | The thing that strikes me the most about SOLID and the associated
       | suite of practices and doctrines ("clean code," "craftsmanship,"
       | etc.) is the complete lack of _evidence_.
       | 
       | What you have is a series of _anecdotes_ from people pushing
       | particular strategies saying that they think the thing they 're
       | saying is a good idea and other things are a bad idea. That
       | doesn't tell you very much about whether the code worked in
       | context, especially on teams of large numbers of developers. It
       | doesn't tell you very much about whether the sample code on a
       | blog that is (allegedly) more readable or understandable is
       | actually more readable or understandable to others.
       | 
       | And many of the folks pushing strategies, giving talks, etc.,
       | seem to be better known for being consultants about how to
       | program than for programming per se. I have, I think, access to
       | more empirical evidence about good and bad programming practice
       | than any blog or talk that a SOLID advocate has given - but it's
       | all internal to my employer's source control repo and you can't
       | see it. If I could, I could easily point to code that has tried
       | to follow "proper" design and has turned into Enterprise FizzBuzz
       | and I could also point to short, readable code that doesn't
       | follow any of the SOLID principles but it doesn't matter because
       | you can read the entire thing in 10 minutes.
       | 
       | Now I admit that I'm not offering evidence either, and as much as
       | I agree with this article, it's also a first-principles argument.
       | It says, for instance, that in our modern era recompiling a
       | system from scratch is cheap and you don't need the open-closed
       | principle, which I strongly agree with, but I can't immediately
       | point at an example of a system that followed the open-closed
       | principle and accreted into cruft, nor at a system that
       | intentionally keeps its internals easy to edit and has worked
       | well.
       | 
       | I think we ought to do something about it as an industry. I don't
       | have great ideas about how. I suppose we could look at open-
       | source code (does Git follow SOLID? Chrome? TensorFlow?
       | OpenStack? what about systems that do the same thing but with
       | very different architectures, like GCC vs. LLVM?). But open-
       | source code is generally subject to different development
       | pressures and expectations than internal code, and the question
       | many of us are trying to answer is, how do we write code at our
       | day jobs that works well?
       | 
       | All that said, I will say this article matches my (anecdotal)
       | experience - and I would go further and agree with the comments
       | that you want to test your system in its entirety, including
       | running the _actual_ database in tests instead of swapping it
       | out. Just like it 's much easier to recompile something now than
       | it was in the '90s, it's much easier to spin up a database in a
       | cloud VM/container/etc. than it was in the '90s. Dependency
       | inversion and injection is a workaround for environments where
       | unit tests are easy but integration tests are hard, and making
       | integration tests easy both solves this and has a range of other
       | benefits for software quality (and development velocity) too.
       | 
       | (And actually there's a potential piece of empirical evidence
       | here: software that doesn't have per-unit licensing costs, either
       | open-source software with no costs or cloud SaaS with continuous
       | billing based on usage, has been beating traditional enterprise
       | licensing. I think you could argue that a big reason for that is
       | that you can spin up that software in a CI environment or on a
       | developer's machine. I can run a local MySQL much more easily
       | than I can run a local Oracle DB, so I'm much more likely to do
       | it, and we're much more likely to go to prod with MySQL, in
       | turn.)
        
         | TuringTest wrote:
         | It's difficult to come up with quantifiable evidence when your
         | method doesn't have a precise measurable definition.
         | 
         | But good practices in a craft don't necessarily need to be
         | quantifiable to be useful. If you learn a group of concepts on
         | how to improve your work, you can ask yourself: "Is any of the
         | SOLID principles applicable here?" and "Is the resulting code
         | better if I apply them?"
         | 
         | If you answer "yes" to both questions, you're using the
         | principles in practice to improve the code without having
         | _empirical proof_ that they worked, just gut instinct.
        
         | jdlshore wrote:
         | Although I'm sympathetic to your complaint, I think it's
         | misguided. I can't think of _any_ design technique or principle
         | that has the kind of objective evidence you're requesting. And
         | yet, I'm still capable of subjectively evaluating a design.
         | 
         | The underlying problem, I think, is that design is a human
         | problem, not a technical one. Design is about making code is to
         | understand and modify. How can its quality be measured?
         | 
         | Without an objective criteria for design quality (and stuff
         | like cyclomatic complexity are far too simplistic) then no
         | study of design principles is possible.
        
       | majkinetor wrote:
       | To me, there is one fundamental principle:
       | 
       |  _Better code is the one that takes lowest time in total to get
       | from point 1 to point N by mediocre developer and in the same
       | time result in lowest amount of help desk tickets_
       | 
       | Its the essence of MIT vs New Jersey design style (i.e. worse is
       | better)
       | 
       | The problem is, its impossible to measure that (unless we tap in
       | parallels universes) which is why design is a form of art in real
       | world.
        
       | pbw wrote:
       | Using the word "wrong" here feels click-baitey when he admits
       | many of the principles are right in certain cases or certain
       | ways. The opposite of every great truth is a great truth. If a
       | principle were true across all space and time with zero
       | exceptions or nuance then software development would be easy.
       | 
       | I very much like the "fits in your head metric" though and I
       | wrote about it at length here:
       | 
       | https://tobeva.com/articles/brain-oriented-programming/
        
       | hankchinaski wrote:
       | to me the whole saga of "clean code" and "solid" it's just
       | pseudo-science predicated by people who have made a fortune
       | selling books and consulting on the subject. unfortunately
       | software engineering it's not an exact science and that's for
       | that specific reason that I rather consume these concepts with a
       | pinch of salt and not take them as axioms
        
       | jesseryoung wrote:
       | As nearly every poster in this thread mentioned, this headline is
       | click-bait. I don't blame the author actually, this is very
       | targeted click-bait (OO programmers) and I took the bait and
       | actually enjoyed the read.
       | 
       | The criticism of SRP hits home for me - I try and write SOLID
       | code when I can and SRP is challenging to follow. I struggle
       | jumping between "Does this really fit SRP?" and "I can probably
       | justify SRP here." I usually settle on the author's "Fits my
       | head" and justify that when doing code reviews with my co-
       | workers.
       | 
       | The author's comments on OCP don't accurately reflect my
       | experience with the field of modern software development. I've
       | worked on many projects (even green-field projects in the last 3
       | years) that have been very expensive and risky to change, where
       | work absolutely needed to be additive because you couldn't trust
       | refactoring tools to handle everything that needed to be changed.
        
         | ssijak wrote:
         | Let me guess, they were Python or Ruby projects?
        
       | dralley wrote:
       | This post is a better summary IMO
       | https://www.reddit.com/r/programming/comments/hhlvqq/its_pro...
        
       | ChicagoDave wrote:
       | I love this, especially the comment on DIP. We went way overboard
       | with dependency injection implementations.
        
       | mdoms wrote:
       | There's a big push across all of Dev Twitter right now to do away
       | with SOLID. To say I think it's misguided is an understatement.
       | There's a similar push underway to do away with unit tests. The
       | direction of our industry right now is very concerning to me. And
       | honestly, this may be an unpopular opinion, but I think a lot of
       | it is driven by people who disagree so vehemently with Bob
       | Martin's politics that they overcorrect and start throwing out
       | his good work too.
        
         | resonantjacket5 wrote:
         | I think the push against SOLID is fine. I've never really seen
         | the 'single-responsibility' part ever really followed or used
         | in a way that made sense.
         | 
         | I haven't really seen a strong push against unit tests?
        
           | drooby wrote:
           | You've never seen SRP in practice? This is honestly
           | concerning.
           | 
           | You've never seen like a User class that only encapsulates
           | fields and methods relating to a User abstraction?
        
         | callmeal wrote:
         | >There's a similar push underway to do away with unit tests.
         | 
         | I agree with that push, as long as there's some other way of
         | validating requirements. In my team, that's done with
         | integration tests and our core principle is: code that is
         | integration tested does not require unit testing. That
         | principle surprisingly covers a good 70-75% of the codebase,
         | leaving us with a few core unit tests for the secret sauce of
         | our product.
        
       | agnosias wrote:
       | Honest question: if you don't do dependency inversion, or if you
       | don't depend on interfaces/abstractions that can't be mocked -
       | how do you unit test your code?
       | 
       | Unit testing is the only reason pretty much all of my code
       | depends on interfaces. Some people seem to consider this a bad
       | thing/over-engineering, but it's how I've seen it done in every
       | place I've worked at.
       | 
       | How do you do it?
        
         | aszen wrote:
         | Firstly if the dependency isn't doing any io, you can test your
         | code as a whole along with its dependency. No need to mock.
         | 
         | More interesting is if your code relies on the outside world,
         | then instead of abstracting out the connection with the outside
         | world abstract out your business logic and test it separately.
         | 
         | So instead of a database repository being injected into your
         | domain services, make your services rely on pure domain objects
         | which could come from any where be it tests or the database.
         | 
         | Make a thin outer shell which feeds data into your domain logic
         | from the outside world and test that via integration tests if
         | necessary.
         | 
         | I'll admit I don't have the full picture here, but I have used
         | this technique to good effect. The core idea is don't embed
         | your infrastructure code deep inside your architecture instead
         | move it to the very top.
        
           | garethrowlands wrote:
           | Some of this is covered in "Functional Core, Imperative
           | Shell", https://www.destroyallsoftware.com/screencasts/catalo
           | g/funct...
           | 
           | Haskell programs tend to have this structure because pure
           | functions aren't allowed to call impure functions.
        
           | globular-toast wrote:
           | > Firstly if the dependency isn't doing any io, you can test
           | your code as a whole along with its dependency. No need to
           | mock.
           | 
           | This the thing. People have a tendency to overuse mocks. The
           | point of automated testing (whether it's unit tests or
           | something else), is to enable refactoring. That's really the
           | only reason. Code that you don't touch doesn't suddenly
           | change its behaviour one day.
           | 
           | In a previous jobs the developers started to go crazy with
           | mocking and it reached a kind of singularity where
           | essentially if a function called anything, that thing was
           | mocked. It definitely tests each function in complete
           | isolation, but what's the point? It makes refactoring
           | impossible, which is the entire point of the tests in the
           | first place!
           | 
           | This excellent talk completely changed the way I approached
           | testing. Every developer who writes tests needs to watch this
           | now! https://www.youtube.com/watch?v=EZ05e7EMOLM
        
             | chakspak wrote:
             | I generally agree with you, though I want to comment on
             | this line:
             | 
             | > Code that you don't touch doesn't suddenly change its
             | behaviour one day.
             | 
             | This can and does happen all the time, when the platforms
             | and abstractions your code builds on change underneath you.
             | This is why a compelling environment / dependency
             | management story is so important. "Code rot" is real. =P
        
               | globular-toast wrote:
               | I thought about this, but technically that is still the
               | code changing, it just happens to be in your dependencies
               | rather than your codebase. The only reason you really
               | have to change dependency versions is security fixes, and
               | they should be infrequent enough that you could do manual
               | testing. So I don't think it's a compelling reason to
               | write unit tests, although it is certainly an added
               | value.
        
         | jdlshore wrote:
         | You can separate your code into "logic" which has no external
         | dependencies (just logic, eg a domain model) and
         | "infrastructure" which does. Infrastructure still requires DI
         | but Logic doesn't.
         | 
         | Some programs are nothing but Infrastructure (DB backed
         | microservices) but some have complex Logic (complex problem
         | domains). In the latter case the most important parts of the
         | system can be tested without DI.
         | 
         | You still need DI but not pervasively.
        
         | drblast wrote:
         | Most of the time I've seen dependency inversion "so we can
         | test" the code is littered with hundreds of single-
         | implementation interfaces and no mocks, or stub mocks that are
         | written just to pass the tests. It doesn't actually test
         | anything.
         | 
         | In the case where there are mocks, the unit tests are almost
         | always literally testing "can my programming language call a
         | function and return a result" or "can we store data in a
         | database if the database works?" They are functional tests
         | disguised as unit tests, and are testing only non-production
         | fake functionality.
         | 
         | Write functional code instead. Liberally use the compiler and
         | the type system to make mistakes impossible instead of unit
         | testing.
         | 
         | Unit tests are for when you can't express something in a way
         | where the compiler and type system will save you from errors.
         | 
         | Everything else is a functional or integration test and should
         | be testing the production system, not a mockup of the
         | production system that works differently.
         | 
         | Basically, write Haskell programs in whatever language you're
         | working in. Use unit tests as a backstop for when the type
         | system isn't as good as Haskell's.
        
         | user_agent wrote:
         | 'How do you unit test your code without mocks?'
         | 
         | I strongly recommend reading "Unit Testing: Principles,
         | Practices, and Patterns" by Vladimir Khorikov.
         | 
         | That's going to answer your question and will unveil a whole
         | world of testing without mocking (not that it's better or worse
         | than with mocks). It might be a single best book about unit
         | testing in general.
        
         | snidane wrote:
         | You are doing interface/function level testing and calling it
         | unit testing.
         | 
         | That's what industry converged on, that a function/method = a
         | unit, but it apparently used to be that a module meant to be a
         | unit.
         | 
         | It can be that both interfaces and modules can be considered a
         | "bag of functions".
         | 
         | Seems like a lot of confusion about unit testing, mocking and
         | DI stems from this historical shift.
         | 
         | I believe that interface/method level testing is too granular
         | and mostly results in overfitted tests. Testing implementation,
         | not behaviour. Which can be useful for some algorithm package
         | for example, but probably not so much applicable to typical
         | business logic code.
         | 
         | TDD: where did it all go wrong. https://youtu.be/EZ05e7EMOLM
        
         | geofft wrote:
         | The good idea of unit testing is repeatable, reliable tests,
         | ideally those that can be run as part of CI before every merge
         | and also on your end-user machine. "Unit" testing imposes an
         | architectural demand on how you do this.
         | 
         | What you actually want is fast and self-contained integration
         | tests. Have an API endpoint that returns some filtered data
         | from the DB? Spawn a DB server locally, load some fixtures into
         | it (e.g., phrased in the form of a migration), start up your
         | application server, and curl it and see if the data is
         | filtered. Then shut it down. In most stacks you can do all of
         | this in a fraction of a second.
         | 
         | The reason most shops love unit tests is that it saves them
         | from having to figure out how to run those integration tests.
         | It's easier from a Conway's Law perspective to use your
         | language's built-in testing facilities to test the function
         | you're writing, let the ops team figure out how one starts up a
         | database server, and let the frontend team figure out how one
         | makes HTTP requests to your application. But you will deliver
         | better software if you spend a bit of time figuring out all
         | those things and stick them in a tiny shell script.
        
         | ItsMonkk wrote:
         | Mark Seemann's blog I've been reading for the past couple years
         | answers this question. In particular, his posts on Hexagonal
         | Architecture[0] and Dependency Rejection[1], but there are many
         | more articles that cover this and I highly endorse reading.
         | 
         | To summarize, you want to set up your code into two distinct
         | categories, data and pure functions. Your data is at the top
         | level of your code, so if you need external data that needs to
         | be at the top level of your module. You then feed that data
         | through a pipeline of pure functions that return with some sort
         | of answer back to your top level module that can then pass into
         | some other function(or through a quick lambda) that does the
         | actual transformation at the top level. The key is that a
         | function can either be a pure function or it can be an impure
         | function. Never both.
         | 
         | As an example, let's say you've got code that has to read from
         | a couple text files. Currently your code does this through a
         | couple nested loops, with the loops passing in what folder it
         | is in and what its file name is, but your code is filtering
         | through some that you don't want to read.
         | 
         | To re-envision this code, you would create a pure function that
         | comes back to the top of your module of which files to read.
         | Then that top module would read those files, and pass them to
         | some other function that does transformations on the result.
         | 
         | You now don't have to create mocks, just create data and pass
         | that into your pipeline and then check if it gets the expected
         | result. One of the interesting side effects of doing this, is
         | at a certain point you stop unit testing for correctness(as the
         | code is so easy to reason about written in this style), and
         | start unit testing primarily for documentation.
         | 
         | [0]: https://blog.ploeh.dk/2013/12/03/layers-onions-ports-
         | adapter...
         | 
         | [1]: https://blog.ploeh.dk/2017/02/02/dependency-rejection/
        
         | madhadron wrote:
         | People have already pointed out using pure functions for the
         | heavy lifting and then making the IO as simple as possible, but
         | another issue is tooling. If you have something like Standard
         | ML's module system or COM style components, you define a
         | functor/interface/contract that your code depends on, and then
         | implement a test form and a production form.
         | 
         | This is basically what dependency injection is doing, but
         | working around the fact that modular programming didn't really
         | catch on.
        
         | Xunjin wrote:
         | I wish to know this as well. And what about functional
         | programming languages?!
        
           | garethrowlands wrote:
           | > Honest question: if you don't do dependency inversion, or
           | if you don't depend on interfaces/abstractions that can't be
           | mocked - how do you unit test your code?
           | 
           | The author isn't actually demanding you change your tests
           | (unless they are unnecessarily complicated).
           | 
           | > Unit testing is the only reason pretty much all of my code
           | depends on interfaces. Some people seem to consider this a
           | bad thing/over-engineering, but it's how I've seen it done in
           | every place I've worked at.
           | 
           | Unit tests require 'seams' to divide the tested code into
           | units but those seams don't have to be interfaces. Some
           | languages don't have interfaces at all - Ruby, say - and they
           | unit test just fine. The pattern of every Foo having a
           | corresponding IFoo _is_ a bad thing - you should replace IFoo
           | with smaller role interfaces. Having an IFoo if it isn 't
           | necessary to test Foo and there's only one implementation is
           | indeed over engineering.
           | 
           | > I wish to know this as well. And what about functional
           | programming languages?!
           | 
           | Unit testing's much the same in functional languages, though
           | it tends to be easier because pure functions are easier to
           | test. But how code gets its dependencies tends to differ a
           | bit. It may help to think of dependency injection as
           | parameterisation. Or, perhaps, parameterisation where you
           | don't use the parameter immediately.
           | 
           | Suppose I want to test a function foo that depends on another
           | function bar:                   fun foo() {
           | return bar()+1         }
           | 
           | Well with objects and interfaces, I can do this:
           | interface IBar {             bar()         }
           | class Bar(): IBar {             fun bar() { return 2 }
           | }              class Foo(val bar: IBar) {             fun
           | foo() {                 return bar()+1             }
           | }
           | 
           | And then I can make a test double for Bar in my test, and
           | when I invoke foo, then it will call the bar of my test
           | double.
           | 
           | Now, of course, in this example, I could test with the real
           | Bar and I don't need a test double. That's partly because the
           | example is simple but also because foo and bar are pure
           | functions. But let's ignore that and see how we can do the
           | same in a functional language.                   fun foo(n:
           | Int) {             return bar()+n         }
           | 
           | The question is, how can we control the value of bar in our
           | tests?
           | 
           | The simplest answer is that, since bar is varying (between
           | test and prod), then bar is a parameter:
           | fun foo(bar: ()->Int, n: Int) {             return bar()+n
           | }
           | 
           | Now the reason we don't do this in OOP languages is that we
           | won't have bar at the call site. That is the production code
           | calls foo like this:                   foo(n)
           | 
           | and not like this:                   foo(myTestBar, n)
           | 
           | That's why our test code looks like this:
           | val myFoo = Foo(myTestBar)
           | assert(myFoo.foo(3)).isEqualTo(something)
           | 
           | though, if foo takes its dependency as a parameter, the test
           | could just look like this:
           | assert(foo(myTestBar, 3)).isEqualTo(something)
           | 
           | But the prod code won't look like that, it only passes n. So
           | we need to pass bar before that. That's why we pass bar to
           | the constructor in the OOP version.
           | 
           | OOP languages generally expect you to pass all the parameters
           | to a function when you call it but some functional languages
           | don't require this. That is, you can pass bar on its own - a
           | mechanism known as 'partial application'.
           | 
           | You'd use it like this:                   // Do this when
           | 'wiring up' the application         val myFoo =
           | foo(myTestBar)              // now I only need to pass n
           | myFoo(n)
           | 
           | If your language doesn't provide partial application, then
           | you can do the same with a function that captures the
           | dependency:                   // Do this when 'wiring up' the
           | application         val myFoo = {n-> foo(myTestBar, n)}
           | // now I only need to pass n         myFoo(n)
           | 
           | Now, notice that, since bar is just a function (of type
           | ()->Int), I didn't need to create a separate interface IBar.
           | For this purpose, functions "just work". They are equivalent
           | to interfaces containing a single function (Java's SAM
           | recognises this, if that's familiar to you).
           | 
           | Notice also that an interface of a single function is as
           | small as it can be. Often, we'll see a class Bar with, say,
           | ten methods, and a corresponding IBar also with ten methods.
           | 
           | SOLID's Interface Segregation Principle says to prefer small
           | 'role' interfaces over obese interfaces such as the ten-
           | methods IBar. And, of course, if you're using functions, that
           | happens naturally.
           | 
           | Hope this helps.
        
             | Xunjin wrote:
             | It does thank you, learnt a bit about partial application
             | when dabbled with Haskell, tho never never really got close
             | to Monad stuff (io I guess?!). Now I'm trying to learn more
             | about Rust also I identified references about "Working
             | effectively with legacy code".
             | 
             | My point being, do you have more references for studying
             | about tests?! As a NodeJs and Python developer today (with
             | almost no tests) it's really hard work with these legacy
             | code :|
        
         | robertknight wrote:
         | An option in some languages is to simply create an alternate
         | mock/fake version of the actual class or function that is
         | depended upon and monkey-patch the code under test to use it
         | for the duration of the test. This is commonly done in Python (
         | see `unittest.mock.patch`) and JavaScript (eg. with Jest mocks)
         | for example.
         | 
         | The end result is the same as if you'd created an interface or
         | abstraction with two implementations (real and fake/mock), but
         | you skip the part where a separate interface is defined.
         | 
         | The upside to this is that the code is more straightforward to
         | follow. The downside is that the code is exposed/potentially
         | coupled to the full API of the real implementation, as opposed
         | to some interface exposing only what is needed.
        
         | h4kor wrote:
         | This was one of the most important lessons in my career.
         | 
         | If you want to test your code thoroughly you needs these
         | techniques.
        
           | tonyedgecombe wrote:
           | Whereas one of the most important lessons of my career is
           | that you don't have to unit test everything.
        
         | stepbeek wrote:
         | I've generally understood the criticism of DI to be levied at
         | codebases where it was applied to literally every class. I'm
         | not the author, but I don't imagine that relying on interfaces
         | to abstract between layers of an application or to avoid a
         | concrete dependency on something like an external api to be
         | what he was arguing against.
        
           | aszen wrote:
           | There are two ways of abstracting out an external api, one is
           | via an interface with a set of methods this is well
           | understood but there is another option as well that is to
           | focus on the core data types your application needs. Create
           | those types and then revolve your whole application logic
           | around them.
           | 
           | Finally create decoders that instantiate those types from
           | different sources.
        
             | stepbeek wrote:
             | This sounds interesting. Can you link to any examples?
        
               | aszen wrote:
               | Go through
               | 
               | https://fsharpforfunandprofit.com/fppatterns/
               | 
               | And
               | 
               | https://fsharpforfunandprofit.com/posts/13-ways-of-
               | looking-a...
               | 
               | I'm still learning on how to apply these ideas in my own
               | work (which doesn't use f sharp or functional
               | programming) but it's a rich source of ideas
        
         | mrloba wrote:
         | Move your logic to pure functions. Use classes for plumbing.
         | Now you can unit test the logic, and integration test the
         | plumbing. You can easily test with a real database using docker
         | for example. Note that you'll still use DI in this case, but
         | you'll have far fewer classes and therefore fewer dependencies
         | as well.
        
         | bandyaboot wrote:
         | That was one of the eyebrow-raising things the author touched
         | on, "automated testing theatre". Really? Unit testing is
         | pointless...is that now a thing? Is there another industry that
         | is constantly changing its mind or debating every aspect of
         | itself quite like the software development industry?
        
           | jdlshore wrote:
           | The author is the creator of BDD. I suspect you're
           | misinterpreting his words.
           | 
           | You can complain about "automate testing theatre" without
           | being against automated testing.
        
             | bandyaboot wrote:
             | Thanks. I'll read some of his stuff on the subject.
        
         | herval wrote:
         | The natural segue for this sort of contrarian "I don't need
         | your patterns" stuff is logically "I don't need unit tests
         | either", soooo pretty sure the answer there is "you don't need
         | tests"
        
           | garethrowlands wrote:
           | Well, he invented BDD, which very much builds on TDD, so, no,
           | that's not the direction he's going.
        
             | herval wrote:
             | Wow that makes it even worse. I had no idea, apologies.
        
         | jnwatson wrote:
         | You pick a language that doesn't require you to bend your
         | architecture around testing.
         | 
         | This is a classic example of software-pattern-being-language-
         | defect.
         | 
         | It is trivial to mock out upstream or downstream dependencies
         | in Python, without any changes to the code under test.
        
       | pfraze wrote:
       | FYI this is not referring to TBL's SOLID project
        
       | king_magic wrote:
       | Yawn. Exceedingly tired of these "X is bad" kind of posts. People
       | can critique things all day - it's hell of a lot harder to create
       | something new, and those are the kinds of pieces I'm interested
       | in.
        
       | shock wrote:
       | Some of the points the author makes are good, but some are
       | severely misguided. An example:
       | 
       | > The Single Responsibility Principle says that code should only
       | do one thing. Another framing is that it should have "one reason
       | to change". I called this the "Pointlessly Vague Principle". What
       | is one thing anyway? [...]
       | 
       | If a change request comes in to change the format of the logs to
       | include milliseconds and you're running a search and replace over
       | the code base, you've broken the SRP. We can argue that is a good
       | thing or a bad thing, but that's what it means. I think it's a
       | bit of both, a trade-off, like most things.
        
       | sorokod wrote:
       | The author rejects "Single Responsibility Principle" as
       | "Pointlessly Vague" and offers instead the "Fits In My Head"
       | principle. That is apparently better due to some hand wavy
       | discussion which leaves no room to explain why same hand wavy
       | justification would not apply to original.
       | 
       | stopped reading after that.
        
         | burade wrote:
         | It's a good point though. Who decides what "one reason to
         | change" is? a 10-line method could easily have hundreds of
         | reasons to change.
        
           | TuringTest wrote:
           | I think the point is that you wouldn't apply those hundreds
           | of reasons all at once. But it your code is doing two things,
           | you could end up having two reasons to change it at the same
           | time.
        
           | sorokod wrote:
           | You the programmer do. Do you expect everything to be
           | automated by successive application of precise rules?
        
         | jonnypotty wrote:
         | I think it's saying simple, dogmatic rules aren't a good idea
         | and don't reduce complexity. Trusting a dev to not write
         | functions that are unreasonably complex IS a good rule if you
         | spend the time to check and review code.
         | 
         | Fitting your code around a load of "best practices" will
         | basically always increase complexity because you've expanded
         | the list of requirements the code has to conform to.
        
           | herval wrote:
           | How is "fits in my head" not dogmatic, though?
           | 
           | And how does fitting around practices necessarily increase
           | complexity? If you're a jr engineer used to write a mess of
           | code to solve a problem, and have to decompose it following a
           | set of principles, you may end up with something SIMPLER very
           | easily.
           | 
           | The whole point about principles is they lead to better
           | outcomes. Problems arise when you confuse them with "rules
           | that can't be broken" or (as in this post's case) just refuse
           | to understand them and come up with your own half-assed
           | heuristics - either to justify your code, for lack of
           | experience, or just... to talk at a conference :-)
        
           | sorokod wrote:
           | I don't think these principles are dogmatic, they rather give
           | you a direction. Can you see that the rule "your code should
           | have single responsibility" is more specific then "write
           | simple code" ?
        
         | garethrowlands wrote:
         | "This article isn't about those principles, that will be my
         | next post."
         | 
         | The article does not present an alternative to SOLID, that's
         | the next article.
        
           | sorokod wrote:
           | ...and the linked slides
        
       | nightowl_games wrote:
       | I spent some time in Web Apps between video game jobs. I got into
       | some tense conversations where I, as manager, recommended against
       | the abstraction layers the Senior Developer was trying to explain
       | to the team on a whiteboard.
       | 
       | He said something like "Why do all the game developers write code
       | like that?".
       | 
       | He handed me Uncle Bob's Clean Architecture. Uncle Bob is
       | something of a meme to me. I read the book and couldn't figure
       | out how to explain how I thought this style was inferior to what
       | I had done in games before.
       | 
       | Ive been thinking about that ever since it happened. How could I
       | explain to this guy my programming philosophy?
       | 
       | Last night, back in games, after refactoring the same piece of
       | layout code for several hours, I finally arrived at the
       | irreducible, simple, clean result that I wanted. I slotted it
       | into our code base, and mentally noted how I would onboard the
       | team to it on Monday, and how they would instantly understand
       | because the resultant code is so simple. Then it came to me:
       | 
       | "Unit Tests are like pouring concrete on your code base."
       | 
       | Same goes for using "the wrong abstraction". Sometimes you want
       | concrete to encase your precious technology. The problem is when
       | people are writing sub par code, then they pour all this concrete
       | on it via Unit Tests, Inheritance and Abstraction Patterns. It
       | can become harder to improve the core logic of the system if it's
       | distributed across many files, and enshrined in Unit Tests.
       | 
       | I've dedicated over 10 years to coding, and only now do I think I
       | can write clean code that is worth encasing in concrete. I think
       | there might be a general flaw in the idea that everyone should be
       | writing tests. I'm not certain about this, it's just my general
       | intuition based on my experience.
       | 
       | They are pros and cons to every design decision. I acknowledge
       | that they are many pros to these kinds of designs, I'm just
       | enumerating some cons that have arisen from my experience.
        
         | villasv wrote:
         | > "Unit Tests are like pouring concrete on your code base."
         | 
         | My experience is the exact opposite. Tests make me feel safe to
         | perform code changes a bit more aggressively than simple
         | refactors. If behavior coverage is slow, I spend more time
         | double checking I'm not breaking stuff along the way.
         | 
         | Sometimes changing implementation will mean changing tests too
         | which might seem to be defeating the point, but this is because
         | the test isn't good yet so it's an opportunity to improve the
         | test and stop leaking implementation details. To me, it pays
         | off handsomely in the long run.
        
           | legulere wrote:
           | If you have real unit tests then refactoring code also means
           | refactoring unit tests, as they just test small units like
           | functions in isolation. When refactoring you will need to
           | replace units or delete them to be able to change units that
           | are higher up in the abstraction. You may end up testing a
           | lot of implementation details and keep them from changing.
           | 
           | If you only write tests of the higher abstracted units while
           | also using the lower abstraction ones, you are not really
           | doing unit testing anymore but what some people call
           | integration testing.
           | 
           | In my opinion they both have their place. Most of the code we
           | write is relatively throw-away code that will be used in one
           | project/occasion and is prone to be changed because of
           | changing requirements. You should test your requirements and
           | functionality with integration tests, unit tests will cost
           | too much time and will stand in your way. In rare cases we
           | write code that is really reusable in a bigger scope and acts
           | like a foundation set in stone that rarely changes and is
           | widely reused. In general you should put way more effort in
           | those and also use unit tests there.
        
             | villasv wrote:
             | I guess what you consider unit testing I consider useless
             | testing, what you call integration testing I call unit
             | testing, and maybe what I call integration testing you
             | would consider end-to-end testing?
        
         | SideburnsOfDoom wrote:
         | > "Unit Tests are like pouring concrete on your code base."
         | 
         | If they're very closely coupled to your code, yes.
         | 
         | I'll just say there's a reason why they're called "unit tests"
         | not "class tests": because a "unit" is not necessarily a
         | "class". You get to choose how big a unit is, and if it impedes
         | change - like pouring concrete - choose differently.
        
       | pron wrote:
       | There's much here I agree with -- and especially with the general
       | lesson of "don't get too hung up on principles," but at one point
       | the article speaks of "1980s entity modelling," in a denigrating
       | way, as if methods of formalisation age. It's like saying that
       | doing logic-style type-level programming or SQL is "1970s logic
       | modelling", and using high-order functions combinators is "1950s
       | functional modelling." Many of the interesting ideas in
       | programming languages from the 1980s haven't yet come of age, let
       | alone grown outdated. Also, it is true that there's much we learn
       | over the years, but also much we forget and reinvent.
        
       | lamontcg wrote:
       | This is a ranty and poor article, and I will continue to use
       | SOLID. As with everything, just mindlessly applying the
       | principles without consider if the resulting code is
       | simpler/better or more complicated/worse is the wrong way to do
       | it.
        
       | Pxtl wrote:
       | TL:dr; SOLID is basically a set of principles saying "keep things
       | small and modular and encapsulated so you can keep them in your
       | head at each layer", and so the specifics of SOLID are silly but
       | the overarching goal is sound.
        
       | hyko wrote:
       | "The Single Responsibility Principle says that code should only
       | do one thing"
       | 
       | Except it doesn't really say that. Without understanding the S,
       | the OLID is bound to seem like mindless pedantry.
        
       | refactor_master wrote:
       | To me this blog doesn't really formally/finally refute anything,
       | but is simply saying "don't over-engineer your solution".
       | 
       | > "dependency inversion has single-handedly caused billions of
       | dollars in irretrievable sunk cost and waste over the last couple
       | of decades"
       | 
       | Oh please. Is there anything in programming that _hasn't_ had the
       | "Irreparable harm to humanity" sticker attached to it by now?
        
         | hinkley wrote:
         | Cloud computing, but only because we haven't hit the trough of
         | disillusionment yet.
        
         | gfody wrote:
         | I like to imagine a final analysis of all code ever written by
         | humans, after some ai hypermind from the future has digested
         | it, turning out to be 99.999% dependency injection boilerplate
        
           | layer8 wrote:
           | Dependency injection has nothing to do with dependency
           | inversion.
        
         | jonnypotty wrote:
         | So becuase our industry is so unprofessional that you can
         | literally point to loads of it and say "that has cost humanity
         | billions", this entire argument is stupid and actually there is
         | no problem at all?
         | 
         | Not sure that's the right attitude.
        
           | AlphaSite wrote:
           | It's an industry which ha s probably generated trillions in
           | value so it maybe a matter of perspective.
        
           | refactor_master wrote:
           | So what would you have done to end up in the alternate
           | universe where it hadn't cost humanity billions? Rational
           | agents and perfect knowledge does not exist outside of
           | economic theories.
        
       | c3534l wrote:
       | I'm not a huge fan of SOLID, but when writing OOP in a
       | conventional style, I have found that it works pretty well. All
       | the principles play nicely with each other. You don't have to
       | write SOLID, but you probably shouldn't write Java code like
       | you're a Haskell developer because that's not a natural or common
       | way to write Java.
       | 
       | Writing simple code is great. For when you can write simple code.
       | For everything else, people invented things like SOLID. We need
       | and want to do complicated things and maintain complicated
       | codebases. If you want to do simple things simply, I'm not even
       | sure OOP is a great model, let alone SOLID, which is an attempt
       | at simplifying people's OO constructions. For all intents and
       | purposes, SOLID is how, in fact, you write simple code in
       | complicated projects.
       | 
       | "Single responsibility" is a much better heuristic than "fits in
       | my head" because code expands and gets bigger. It fits in your
       | head now, but no decently sized, actively developed project stays
       | simple. When do you break it up? When it starts taking on
       | multiple responsibilities. Yes, that isn't objective. But the
       | alternative proposed is even less objective. Additionally, "fits
       | in your head" is inferior because it assumes you already
       | understand the codebase. If you are new to the code base, trying
       | to weave through the functions and objects called to figure out
       | what it does, looking at code that does multiple things is
       | confusing and hard to synthesize without first understanding the
       | whole. And you can't understand the whole until you understand
       | the pieces. Single responsibility is much more specific and
       | identifiable than the author's alternative.
       | 
       | The author mis-attributes the Open-Closed principle to a
       | performance optimization. The open-closed principle allows me to
       | make assumptions about what a class is doing and how it behaves.
       | This is because I know it hasn't been modified. Wikipedia notes
       | that the original formulation of the term, a closed class or
       | module "has been given a well-defined, stable description." The
       | point here is that if you don't let something be fundamentally
       | changed during runtime, then you can reason about it. It is
       | definitely not a performance hack.
       | 
       | The author states that the Liskov Substitution Principle is the
       | principle of least surprise, which is surprising as you cannot
       | substitute one principle for the other. This suggests to me that
       | the author does not actually understand the meaning of this
       | principle, especially as he fails to give a substantive critique
       | and unhelpfully declares that you should write simple code. The
       | Liskov Substitution principle is, again, how we reduce the
       | apparent complexity of code so it allows us to reason about it
       | more effectively.
       | 
       | This next one is baffling. The author of this article recounts
       | the anecdote showing an instance where the interface segregation
       | principle would have been helpful in designing the code. However,
       | the author appears to be unable to gleam from that anecdote the
       | underlying principle being advocated and is fixated on the fact
       | that in the story the code was refactored. Since the anecdote
       | describes an implementation, the author is unable to see it as
       | anything other than a design pattern.
       | 
       | The author claims this principle has cost billions of dollars in
       | damage. He does not feel the need to explain this or provide a
       | citation. Just, you know, billions of dollars in damage. Like we
       | just all know that this powerful decoupling principle just set a
       | large fortune on fire and this is plainly obvious to everyone.
       | Next he says "the real principle here is option inversion." No,
       | it isn't. I don't know where the author got this from. But I
       | suspect given what he's said previously that it just sort of made
       | sense to him and then decided that must be what this principle is
       | about without any further discussion. But the author is none the
       | less actually correct that you can over-use this principle to
       | over-complicate code before it really needs to be that
       | complicated. These are, of course, ultimately rules of thumb that
       | work well together. Common sense and experience are still
       | required.
       | 
       | Robert C. Martin has an unusual habit of giving advice that
       | sounds kind of stupid at first glance, until you actually try it
       | out for yourself. His "rules" often have hidden reasons behind
       | them that only become clear when you write code with them. I
       | suspect that the author did not give SOLID a serious shake as his
       | criticisms all sound strongly like someone who read about SOLID,
       | failed to internalize their meaning or give the technique a try,
       | and is now attacking a strawman with advice that, quite unlike
       | Robert C. Martin's style, sounds okay at first glance, but you
       | quickly realize does not translate into actionable advice when
       | you're coding. I've heard "fits in your head" before. It sounds
       | like what you want, but it does not allow you to identify
       | problems early and predict what sorts of changes you might need
       | to make to the code to prevent those problems. Especially if
       | you're a beginner, try writing according to SOLID principles. See
       | if it helps. After you understand why those are the principles
       | and why they all go together by seeing what sorts of decisions it
       | causes you to make in your own code, you're free to ignore it
       | having at least learned what the advice actually is.
        
       | gtsop wrote:
       | The author inverted the way we rationaly understand reality.
       | Instead of observing and concluding, he concluded and then retro-
       | fitted observations - he actually admited this by stating his
       | motivation was to make an anti-solid presentation for the sake of
       | it, I'm not making this up.
       | 
       | Thus it is unfair for us to try and contradict his arguments,
       | it's like talking to a conspiracy theorist, they will glue random
       | facts trying to support a pre-decided conclusion.
       | 
       | Having said that, this post is very shallow. Even if there is
       | merit to criticising the SOLID principles (and I would be very
       | interested in such a criticism), you can't just do it an a couple
       | of sentences.
        
         | andybak wrote:
         | Your reply makes it sound like you missed the tone and context
         | of the post. Light-hearted and intended to poke a few sacred
         | cows...
         | 
         | But also you seem to be denying two important possibilities: 1.
         | Something can be both flippant and insightful. 2. A "couple of
         | sentences" is sometimes all that is needed to trigger a light-
         | bulb moment in someone else's head.
         | 
         | We're not writing legal treatices here. We're trying to
         | transfer insight from one conscious brain to another using an
         | imperfect medium.
        
           | gtsop wrote:
           | > Your reply makes it sound like you missed the tone and
           | context of the post. Light-hearted and intended to poke a few
           | sacred cows...
           | 
           | If that is the context and tone, then I must admit I missed
           | it.
           | 
           | > But also you seem to be denying two important possibilities
           | 
           | I don't disagree this can happen. Let's see what the future
           | will bring. Still I feel the writer could have done a better
           | job going into more detail.
        
         | burnished wrote:
         | What? The post isn't a scientific article, its a persuasive
         | piece. The hypothesis -> testing -> results model of
         | understanding is great for problems it can be easily applied
         | to. Design doesn't seem to be one of those.
        
       | DanielBMarkham wrote:
       | 1. Watching the tech community over a really long time, please
       | guys, don't swing violently from "A is all good!" to "A is all
       | bad!" It was good for something, else so many people wouldn't
       | have successfully used it for so long. Work more on
       | discrimination functions and less on hyperbole please. Future
       | generations will thank you for it.
       | 
       | 2. "...coupled with the way most developers conflate subtypes
       | with subclasses..." Speaking as somebody who both likes SOLID and
       | could write this essay/present the deck, I think there's a lot of
       | confusion to go around. There are a lot of guys coding classes
       | using rules better suited for types. There are a lot of guys
       | applying OO paradigms to functional code and vice-versa. In
       | general, whenever we swing from one side to the other on topics,
       | it's a matter of definitions. There is no such thing as "code".
       | There's "thinking/reasoning about code" and there's coding. You
       | can't take the human element out and reason abstractly about the
       | end product. Whatever the end product is, it's a result of
       | one/many humans pounding away on keyboards to get there.
       | 
       | 3. My opinion, for what it's worth: as OO took off, we had to
       | come up with heuristics as to how to think about grouping code
       | into classes, and do it in such a way that others might
       | reasonably get to the same place ... or at least be able to look
       | at your code and reason about how or why you did it. That's SOLID
       | and the rest of it. Now we're seeing the revenge of the FP guys,
       | and stuff like SOLID looks completely whack to them, as it
       | should. It's a different way of thinking about and solving
       | problems.
       | 
       | ADD: Trying to figure out who's right and who's wrong is a
       | (philosophical) nonsense question. It's like asking "which smell
       | is plaid?" Whatever answer you get is neither going to provide
       | you with any information or help you do anything useful in the
       | future. (Shameless plug: Just submitted an essay I wrote last
       | week that directly addresses reasoning about types in a large
       | app)
        
         | chrisseaton wrote:
         | > It was good for something, else so many people wouldn't have
         | successfully used it for so long.
         | 
         | I don't know - some ideas just turn out to be completely
         | mistaken and any success while using them to be actually down
         | to something else entirely.
        
         | cghendrix wrote:
         | +1 on the hyperboles. You see so many articles with titles like
         | "Never use a singleton unless you want to lose your marriage
         | like me"
        
           | [deleted]
        
         | elric wrote:
         | > don't swing violently from "A is all good!" to "A is all
         | bad!"
         | 
         | Indeed. This clickbaity style of laying out arguments is not
         | terribly constructive. Software is not black/white. It's
         | entirely grey. And there's a lot of room for contextual nuance
         | _everywhere_.
         | 
         | Principles like SOLID (and DRY, and YAGNI, etc) are
         | _principles_. They are not _laws_. Principles are guidelines
         | which can help you make solid (heh heh) decisions. They are
         | subject to _context_ and _judgement_.
         | 
         | If good software design were as easy as memorizing a couple of
         | acronyms, we'd all be out of a job. But it's not. It takes
         | practice and experience. Writers and academics can make things
         | easier by presenting accumulated experience in principles and
         | guidelines, but there are no silver bullets. It's unfair and
         | pointless to expect SOLID (or anything else) to apply in any
         | and all cases.
        
           | dvlsg wrote:
           | > It's unfair and pointless to expect SOLID (or anything
           | else) to apply in any and all cases.
           | 
           | I think that's a big part of the problem, and how we end up
           | with articles like this. A lot of developers do expect SOLID
           | to apply to every case, and I've seen fine code get rejected
           | in reviews because it wasn't SOLID enough.
        
             | no-s wrote:
             | >>A lot of developers do expect SOLID to apply to every
             | case, and I've seen fine code get rejected in reviews
             | because it wasn't SOLID enough.
             | 
             | Prescriptive principles without informed discretion. That
             | is the problem, and it's not just software.
             | 
             | "A foolish consistency is the hobgoblin of little minds."
             | 
             | It's terrifying how easily a reasoned and seeming sensible
             | polemic may be interpreted into a foolish oppression, even
             | by otherwise rational folk...
        
             | [deleted]
        
           | drewcoo wrote:
           | A thousand times this!
           | 
           | Principles, heuristics, "best practices," and just generally
           | good ideas are not absolute truth. SOLID is like hand
           | washing. Please do it! Unless you have a good reason not to.
           | 
           | The root of the "disprove a heuristic by a single
           | counterexample" problem is a misunderstanding of logic. A
           | heuristic is not a statement that universally all hands must
           | always be washed. It is a milder claim that generally
           | handwashing has proved useful via inductive means, so you
           | should probably wash your hands if you want to minimize your
           | risk.
           | 
           | Any expert in a given field should know times when not
           | washing hands has been justified. But by the same token,
           | those people know that they should still recommend hand
           | washing to the general public because they won't know when
           | it's not justified.
           | 
           | Wash your hands, please.
        
           | cratermoon wrote:
           | > Software is not black/white. It's entirely grey
           | 
           |  _entirely_? There aren 't parts that are black/white? That's
           | seems a bit black/white.
        
             | wizzwizz4 wrote:
             | Ah, but that comment isn't software, is it?
             | 
             | [0]: https://esolangs.org/wiki/English
        
             | aroman wrote:
             | Let's settle on it being a gradient from black to white.
        
             | andrewprock wrote:
             | Well, the 0s are black and the 1s are white, except when it
             | is the other way around.
        
             | elric wrote:
             | Touche. "Only the Sith deal in absolutes" and all that.
        
         | youerbt wrote:
         | Agree. Programming is just too vast of a field to have
         | universal principles. It is also changing to fast for most
         | principles to remain relevant.
         | 
         | We just like mental shortcuts. We ask should we use
         | SOLID/whatever, instead of asking why was SOLID/whatever used
         | and does this why apply to us.
        
         | lostcolony wrote:
         | "It was good for something"
         | 
         | The post does seek out where the SOLID principles came from.
         | And it's not really debunking them; just saying they're not
         | absolutes. Which, yes, the title is click-baity, but I've
         | certainly found people who treated them as absolutes, or tried
         | to talk about code from SOLID perspective, and I've certainly
         | never found that useful.
         | 
         | In fact, I've not found -any "best practice" to ever be
         | absolute in a generalizable sense, and it's never been useful
         | rhetoric to bring them up in a design discussion because of
         | that. In fact, they sometimes run counter to each other. "DRY
         | would say we should combine these together" - "Yeah, but Single
         | Responsibility; while the code is mostly the same, they
         | fundamentally are dealing with different things in different
         | contexts".
         | 
         | Learn the heuristics, then deal with the subjective realities
         | each project brings; anyone who tries to treat a given codebase
         | as having an objectively correct approach, rather than a
         | nuanced and subjective series of tradeoffs, is not someone
         | worth talking or listening to.
        
           | dllthomas wrote:
           | > "DRY would say we should combine these together" - "Yeah,
           | but Single Responsibility; while the code is mostly the same,
           | they fundamentally are dealing with different things in
           | different contexts"
           | 
           | As originally coined, DRY speaks of ensuring every _piece of
           | knowledge_ is encoded once. If pieces of code are  "dealing
           | with different things" then those are two pieces of
           | knowledge, and DRY (per that formulation) does not recommend
           | combining them.
           | 
           | I agree that there is a prevalent notion of DRY that is more
           | syntactic, but I find that version substantially less useful
           | and so I try (as here) to push back on it. Rather than
           | improving code, it's compressing it; I've joked that we
           | should call it "Huffman coding" when someone tries to
           | collapse unrelated things in ways that will be
           | unmaintainable.
           | 
           | Note that it's not just that syntactic DRY sometimes goes to
           | far - it also misses opportunities where the original DRY
           | would recommend finding ways to collapse things: if I'm
           | saying "there's a button here" in my HTML and in my JS and in
           | my CSS, then I'm saying the same thing in three places (even
           | though they look nothing alike) and maybe I should find a way
           | to consolidate.
           | 
           | There are, of course, still tradeoffs - maybe the technology
           | to unify the description isn't available, maybe deepening my
           | tech stack makes things less inspectable, &c.
        
             | lostcolony wrote:
             | I posted to a sibling comment to yours, but wanted to say
             | here too - at that point it ceases to be a useful statement
             | to ever bring up, because I've never seen a discussion
             | where everyone agreed things were the same 'piece of
             | knowledge', and one side was saying it should be repeated.
             | When I've heard "DRY" trotted out, it's -always- been in a
             | situation where the other side was trying to claim/explain
             | that they were different pieces of knowledge. Hence my
             | statement - it's worth understanding the meaning of the
             | principle, internalizing the lesson, as it were, but then
             | the formulation ceases to be useful.
        
               | dllthomas wrote:
               | Interesting. Our experiences differ dramatically.
               | 
               | In my experience, misguided "could this be DRYer" is
               | usually motivated by superficial similarity, and a focus
               | on knowledge is clarifying. (Although as I mentioned,
               | "yes, it's repeated, but given tradeoffs that's the best
               | in this case at least for the moment" is still possible.)
        
               | lostcolony wrote:
               | Oh, no, I'm agreeing with - throwing out the statement,
               | and instead focusing on whether or not it's a shared
               | context, is everything. Once you understand it's the same
               | context, you -know- what to do; you don't need to be
               | reminded that DRY is a virtue
        
             | no-s wrote:
             | >>I've joked that we should call it "Huffman coding" when
             | someone tries to collapse unrelated things in ways that
             | will be unmaintainable.
             | 
             | heheh I've made that joke too, also "let's un-complicate
             | this into a pointless complexity," when its goes over their
             | head.
        
           | cjblomqvist wrote:
           | I understand that this is almost nitpicking (because the DRY
           | example is not the point of your comment), but your DRY
           | example is a really bad example of this, but rather a very
           | good example of the lack of knowledge within the software
           | community. According to Wikipedia, DRY means "Every piece of
           | knowledge must have a single, unambiguous, authoritative
           | representation within a system" [1], NOT to deduplicate all
           | code that looks the same. It's actually more or less exactly
           | the same as the Single Responsibility principle.
           | 
           | PS. Interviewing senior devs, team leads and tech leads atm.
           | And so far none (!) have been able to properly formulate this
           | (even after I'm hinting that it's not only about code
           | deduplication) and 75-90% believe it's all about code
           | deduplication. Imo quite scary, and tells you a fair bit
           | about the shape of the software dev industry...
           | 
           | [1] https://en.m.wikipedia.org/wiki/Don%27t_repeat_yourself
        
             | lostcolony wrote:
             | Yes, but at that point it stops being a heuristic and
             | instead is a tautology. "Don't repeat the things that
             | shouldn't be repeated". Or even what you said, which,
             | again, is a nice statement, but who decides what 'a piece
             | of knowledge' is? I.e., when is it the same bit of
             | knowledge, vs when is it different? That's often the heart
             | of it; I've had those debates (and in fact, was referencing
             | them in my parent comment), where someone feels these
             | ~things~ are basically the same, and so should be treated
             | mostly the same, with shared code, and where someone else
             | feels that, no, the differences are sufficient that they
             | should be treated differently. And that's a reasonable
             | discussion to have. But it's one that trotting out "Don't
             | Repeat Yourself!" or SOLID or etc adds -nothing- to; the
             | principles themselves clash, and ignore the core difference
             | you're trying to work out.
             | 
             | In short, the reasons for the principle matter, but if you
             | know to look for and understand the reasons, the principles
             | themselves are obvious and do not serve as useful
             | heuristics.
        
               | cjblomqvist wrote:
               | What is a piece of knowledge, who decidesn: A piece of
               | knowledge in the domain. Of course there can be
               | discussions around that as well, but should clear up the
               | most obvious mistakes?
               | 
               | Principles and concepts: The reason for naming things is
               | to be able to efficiently communicate things, make them
               | memorable, etc? For example when communicating things in
               | an interview? Why do we name things generally? Empire
               | State Building? Or just the building on Xth street/avenue
               | in NYC. Then of course, if everyone is misinterpreting
               | the Empire State building for the Statue of Liberty then
               | either the naming was off, or the teachings of the
               | name....
        
               | lostcolony wrote:
               | Here's a very specific example I had -
               | 
               | A senior dev was creating an admin tool that took 7 or so
               | different things, and treated them the same. They were
               | very similar in how they operated, but there was some
               | complexity.
               | 
               | Cue two months of MTTF never changing; squash a bug,
               | introduce a new one.
               | 
               | I, as a junior, opted to rewrite everything. Got manager
               | buy in. Did so, largely just separating these areas,
               | treating them as unique things. Voila. MTTF started
               | dropping.
               | 
               | Everyone agrees if it's the same thing, don't repeat
               | yourself; no one thinks repeating themselves is a virtue.
               | So once you understand that it's worth trying to find the
               | things that are the same bits of context, knowledge, etc,
               | as part of basic understanding of the domain, you
               | understand the reasons for DRY, and the phrase ceases to
               | need ever be uttered (and in fact, becomes counter
               | productive, because it is to try and say "best practice
               | would be to not repeat this!" without actually addressing
               | the real issue at stake; are these the same thing?)
        
             | no-s wrote:
             | >>none (!) have been able to properly formulate this (even
             | after I'm hinting that it's not only about code
             | deduplication) and 75-90% believe it's all about code
             | deduplication
             | 
             | Well, duplicate code is the typical manifestation, as the
             | sibling comment [1] relates:
             | 
             | >>> if I'm saying "there's a button here" in my HTML and in
             | my JS and in my CSS, then I'm saying the same thing in
             | three places (even though they look nothing alike) and
             | maybe I should find a way to consolidate.
             | 
             | "and maybe" it's just otherwise hard to get the point
             | across, as you've discovered. Isomorphisms are easier to
             | distinguish (and validate!) versus homeomorphisms, but
             | unnecessary pedantry usually results in MEGO. We shouldn't
             | expect senior developers to automatically embody the
             | virtues of mathematicians or philosopher-kings...
             | 
             | yeah, I am nitpicking worse, but after 40+ years in the
             | industry and suffering through tens of thousands of
             | marginally relevant distinctions in "gotcha" interview
             | questions, I am without shame even though my head is
             | nodding in acknowledgment of your points...
             | 
             | [1] oops, I meant
             | https://news.ycombinator.com/item?id=26532424
        
               | cjblomqvist wrote:
               | I'm not really following you but intrigued. A few
               | questions: You're quote of the parent doesn't seem
               | correct? Where did you find that quote? MEGO?
               | 
               | This is imo not a gotcha interview question. It's A)
               | Intended as a way for me to check if the person has an
               | understanding of when to deduplicate code or not (I don't
               | care if they know what DRY is, and I try to help them
               | without giving them the straight up core part of the
               | answer, give examples, etc). I believe this is one of the
               | fundamental parts of writing maintainable code and B)
               | Able to formulate this so that others can learn from
               | them. Both are critical skills for the role.
               | 
               | In my experience, by applying DRY wrongly you're creating
               | a really dangerous code base (maintainability wise). It's
               | probably even worse to deduplicate code wrongly than not
               | deduplicating at all.
        
               | no-s wrote:
               | >this is one of the fundamental parts of writing
               | maintainable code
               | 
               | >It's probably even worse to deduplicate code wrongly
               | than not deduplicating at all.
               | 
               | I strongly agree.
               | 
               | >A few questions: You're quote of the parent doesn't seem
               | correct? Where did you find that quote? MEGO?
               | 
               | pardon me, I meant a sibling [1], not the parent comment.
               | Otherwise I'm just relating my experience. MEGO is an
               | acronym for "my eyes glaze over", I see searching on it
               | leads to a LEGO clone, thanks Google. Trying to explain
               | why a homeomorphism is potentially a DRY violation is the
               | kind of explanation that leads to MEGO.
               | 
               | I don't like those "hinting" questions any more, in
               | retrospect it seems more about exploiting assumptions of
               | superiority at being obscure rather than a valid
               | assessment of suitability. Also seems to waste precious
               | time (interviews cost time, more valuable than money).
               | Better to be direct and practical, principally because
               | misunderstanding is more likely the default state of
               | human transactions. It is probably better to first settle
               | on a mutual understanding of some expected conceptual
               | requirement (e.g. understanding of DRY), then produce
               | some specific examples for the interviewee to judge and
               | interpret. Since these are mostly heuristics, judgement
               | with respect to application of heuristics is the key
               | facility I would attempt to elicit, InMyHumbleOpinion (no
               | more TLA, CamelCase from now on!).
               | 
               | [1] https://news.ycombinator.com/item?id=26532424
        
             | andrewprock wrote:
             | Well code _is_ data, so it 's understandable that things
             | get squishy inside people's heads.
        
         | spaetzleesser wrote:
         | In politics and in tech consulting there is a lot of money and
         | fame to be made by going to the extremes and not allowing the
         | middle ground. I just wish people wouldn't constantly fall for
         | this, be it in politics or in tech.
         | 
         | Wait another 10-20 years and FP will suffer the same fate.
        
       | tcgv wrote:
       | The issue with SOLID is not that it's wrong or outdated, it's
       | that some people think it's something you learn in five minutes
       | by reading its Wikipedia page, which it isn't. It takes a lot of
       | effort to develop skills to adopt SOLID effectively.
       | 
       | > When I look at SOLID, I see a mix of things that were once good
       | advice, patterns that apply in a context, and advice that is easy
       | to misapply. I wouldn't offer any of it as context-free advice to
       | new programmers.
       | 
       | Off course! I wouldn't offer any OOP architectural/programming
       | principle as context-free advice to new programmers. It's easy to
       | misinterpret it if you don't contextualize it, show how it's used
       | in real projects, discuss advantages and disadvantages, and so
       | forth.
       | 
       | > The Single Responsibility Principle says that code should only
       | do one thing. Another framing is that it should have "one reason
       | to change". I called this the "Pointlessly Vague Principle". What
       | is one thing anyway?
       | 
       | This gives me the impression that the author indeed
       | misinterpreted this principle, just to name one, oversimplifying
       | it. If you like the author have trouble understanding the Single
       | Responsibility principle, take a look at "On the Criteria To Be
       | Used in Decomposing Systems into Modules" by David Parnas [1].
       | 
       | It seems to me that the author also oversimplified the other
       | principles as well, maybe on purpose, for using controversy to
       | attract attention to his post.
       | 
       | [1]
       | https://www.win.tue.nl/~wstomv/edu/2ip30/references/criteria...
        
       | jacques_chester wrote:
       | > _Code is not an "asset" to be carefully shrink-wrapped and
       | preserved, but a cost, a debt. All code is cost._
       | 
       | The analogy to accounting is still useful and I believe correct.
       | Assets depreciate (lose their value) and have carrying costs
       | (storage, insurance, interest etc) that are booked as expenses.
       | 
       | Put another way, a phrase like "all existing code _incurs_ costs
       | " is an accurate analogy. So too is "all code is an asset",
       | because it implies the former.
        
         | sorokod wrote:
         | Sure, but replacing code with new (cheaper?) code has cost in
         | itself. All the author has to offer here is a recommendation to
         | "Write simple code", presumably one that follows his "fits in
         | my head principle".
        
           | jacques_chester wrote:
           | > _Sure, but replacing code with new (cheaper?) code has cost
           | in itself._
           | 
           | Of course. That's true of many assets, usually this is called
           | the "replacement value" of the asset. You use replacement
           | value and depreciation to calculate whether replacement is
           | the best option, or whether to extend the life of the asset
           | you already have.
           | 
           | Tockey's _Return on Software_ is a fairly readable guide to
           | some of these topics: https://www.amazon.com/Return-Software-
           | Maximizing-Your-Inves...
        
       | FpUser wrote:
       | I do not waste my time trying to prove / figure out what is the
       | "proper" way of doing things. I use whatever approach I feel
       | suits better for particular case and do not give a flying hoot
       | about what's the latest fashion.
        
       | coldtea wrote:
       | SOLID is folk wisdom to begin with. Most of developing (as
       | opposed to computer science) is unresearched old-wives-tales and
       | cargo cult.
       | 
       | Is SOLID any good? Who knows? Who proved it? Heck, who even did
       | tests?
        
       | js4ever wrote:
       | Loved it! I agree with most of it as well, I'm so fed up with
       | preparing with abastractions to things that will probably never
       | occur (changing the underlying DB for eg) I changed my coding
       | style a decade ago to focus on producing code that is as simple
       | as possible to read/maintain with as few abastractions layers as
       | possible.
        
         | Lewton wrote:
         | I know of at least one company that's slowly dying because
         | their product is irreparably tied to oracle
         | 
         | Literally impossible to get any new clients to agree to run on
         | oracle
        
         | hyko wrote:
         | Providing you're correct about the DB choice outlasting the
         | lifetime of the software, there is nothing about that approach
         | that's incompatible with SOLID.
         | 
         | Ultimately the goal of SOLID is to be able to change any
         | important aspect of the software independently from any other.
         | If the DB is going to outlast the business logic you're
         | writing, there's no problem having it depend on the DB
         | concretely.
         | 
         | Separating the _what_ and the _how_ does have the effect of
         | changing how you approach reasoning about a program though, so
         | SOLID is not without penalty.
         | 
         | Edited for clarification.
        
         | toddh wrote:
         | I'm curious what other people think when they take over your
         | code? We all have a bias to think our way is simple and
         | obvious, yet it rarely works that way in practice.
         | 
         | Abstractions are a way of trying to find a common ground
         | conceptually. Agreed, abstractions should not be multiplied
         | unnecessarily, but in the same way it's easier to learn
         | Newton's Laws than read Kepler's data, a few well chosen
         | abstractions help others organize and understand what's
         | happening in a code base.
        
           | deckard1 wrote:
           | > Abstractions are a way of trying to find a common ground
           | conceptually.
           | 
           | What's always amused me, in a tortured sort of way, is that
           | SQL is already _the abstraction_. Then people go and layer an
           | abstraction on top of it, such as some ORM flavor-of-the-day,
           | which is obviously much less universal than SQL. In  "SQL"
           | I'm including vendor extensions too (Oracle, MySQL, etc.).
           | It's easier to read up on vendor extensions than learn yet-
           | another ORM.
           | 
           | Which do you think has had the longer shelf life: MySQL or
           | some random Ruby ORM? If you've been doing MySQL since the
           | '90s then it's largely the same as MySQL of 2021. That Ruby
           | ORM? Probably hasn't seen an update since 2008. I can't even
           | remember the _names_ of all the ORMs I 've had to use over
           | the years.
        
             | toddh wrote:
             | I guess I don't think of an ORM as abstraction, it's more
             | of a complication. The abstraction would be something like:
             | 
             | class User {                  static get(id) {
             | embed your SQL here        }     }
        
         | thatwasunusual wrote:
         | > I'm so fed up with preparing with abastractions to things
         | that will probably never occur (changing the underlying DB for
         | eg) [...]
         | 
         | How can you be fed up with something that will never occur?
        
         | majgr wrote:
         | I, once, removed a graph from application. Application has all
         | these nice abstractions. Change touched 40+ files. Single
         | Responsibility? bonkers.
        
         | herval wrote:
         | Reading these comments make me feel like a huge outlier.
         | Practically every project I ever worked on included AT LEAST
         | one database swap. That includes startups and big tech, for all
         | sorts of different reasons.
        
           | geofft wrote:
           | I've worked on projects with database swaps, too, but I find
           | it hard to believe that use of abstraction in advance would
           | have helped them. There's a couple of cases.
           | 
           | One is that you're using two SQL databases and you're not
           | using any advanced features. You started dev on MySQL, and
           | then the company says "Thou shalt use Postgres" (or
           | whatever). You don't need anything fancy in your own code to
           | handle this. You're still making SQL queries, you're just
           | swapping out the database engine. Technically, this is an
           | example of dependency inversion (depend on the SQL
           | abstraction), but you also didn't set out to do it -
           | basically any programming language you're likely to use has
           | the common database libraries use a common abstraction for
           | sending queries. And you didn't specifically make sure you
           | were writing generic SQL, you just happened not to need
           | anything.
           | 
           | More commonly, you're switching databases for a specific
           | feature. Maybe you realize PostGIS (or whatever) is going to
           | solve a problem for you very well. But then you're changing
           | how you model data, what your schemata are, and even how your
           | code is architected and accesses things in the database.
           | You're deciding to move certain logic from your code to the
           | database engine - might even decide to move certain logic
           | from the frontend into the backend, or change how request
           | routing works, or something. This is a fantastic reason to
           | move databases, but no amount of abstraction can prepare you
           | for it, because you're fundamentally changing what the
           | abstraction is. And you're deliberately abandoning SOLID
           | because you're picking up a dependency on a concrete
           | database.
           | 
           | But the real case I've seen is where you're switching
           | databases (or data storage layers, more generically) to a
           | different model - MongoDB to not-MongoDB, a C/P database to
           | an A/P one, a relational one to a key-value store, etc. This
           | is the above case but even larger. There is no abstraction
           | you could possibly write that could encompass the old and new
           | cases. It requires rearchitecting how your code works.
           | 
           | And then there are the most boring of cases - the ones where
           | a database swap sounds doable in theory and the code is
           | supposedly using an abstraction layer, but no one has ever
           | verified that the code doesn't make assumptions about what
           | database it's on and the code has gotten too big, so we just
           | get an architectural exemption from "Thou shalt" and we run
           | our own instance of the wrong database, because the overhead
           | of running our own DB costs the business less than getting
           | the swap wrong.
           | 
           | (Some public examples of these sorts of database migrations
           | that come to mind: https://slack.engineering/scaling-
           | datastores-at-slack-with-v... is about how Slack couldn't
           | move away from MySQL and the architectural assumptions they
           | made about it and had to rule out migrations to non-
           | relational databases out of hand, and
           | https://about.gitlab.com/blog/2018/09/12/the-road-to-
           | gitaly-... talks about how GitLab moved from NFS storage to
           | an RPC service, requiring a lot of refactoring of callers.)
        
             | herval wrote:
             | I've been through plenty of cases where companies
             | successfully swap out databases exactly as you described -
             | a document storage for an Rdbms or vice-versa. The bird
             | social network is an example where the db was so well
             | abstracted, they managed to swap these out with no need to
             | rewrite any application code. So does Facebook, for
             | instance. Slack is a clear example of what the complete
             | lack of forethought on this leads to. (Disclaimer: I'm
             | familiar with all 3, but obviously can't talk details -
             | there's plenty of public posts on these cases, though)
             | 
             | FWIW, every single db abstraction I've ever witnessed was
             | worth it - if only so that one could run tests in a sqlite
             | and run prod in something else, or as a way to contain
             | vendor lockin in the code (I've seen projects successfully
             | migrate from a plsql-heavy system to mysql because the code
             | was well segregated, and I worked at a startup that
             | literally imploded bc the database was metastasized all
             | over the place)
             | 
             | Anyway, as I put it, abstracting data storage is a no-
             | brainer for me, and it saved my skin every single time. I
             | don't expect to convince anyone here to go do it. :-)
        
               | geofft wrote:
               | I think that's changed my mind a little bit on this,
               | thanks! I'm mostly surprised they were able to get the
               | abstraction right in advance - did they have an idea of
               | what they might move to? Or did they change the
               | abstraction as they went, but the abstraction layer made
               | it easier to find what parts of the code needed changing,
               | or something?
               | 
               | How do you avoid the issue where SQLite doesn't support
               | all the things your real DB supports? (i.e., why is it
               | worth running tests on SQLite as opposed to a local
               | instance of the same DB software?)
               | 
               | I'll read some of these public posts....
        
               | ivan_gammel wrote:
               | >>the db was so well abstracted, they managed to swap
               | these out with no need to rewrite any application code
               | 
               | What were the reasons for the swap and what were the
               | outcomes of it besides that it actually happened? Costs,
               | performance, scalability etc?
               | 
               | I think hardly anyone would argue that those transitions
               | are impossible, because they are indeed possible. The
               | main question is usually if your abstraction can leverage
               | the benefits of underlying implementation or it is just a
               | common denominator. I can imagine a clever technical
               | strategy in a startup, foreseeing future growth and
               | starting with a simple DB before moving to a more complex
               | in maintenance but scalable solution. This may work.
               | Often it doesn't.
        
               | herval wrote:
               | Varies from project to project of course, but it's never
               | "just because". If you're swapping dbs without reasonable
               | expectations to improve SOMETHING, it is indeed a waste
               | of time (one that I also fought against in my career many
               | times - been through many, many "let's adopt MongoDB bc
               | it's hot" cases)
        
         | ivan_gammel wrote:
         | That's one of the traps, every programmer will fall into at
         | least once: copying some abstraction because someone else did
         | it. I have not seen support of changing underlying DB as a
         | business requirement yet (well, outside of frameworks and
         | platforms, of course). I have seen many times when developers
         | designed architectures, created APIs or added buttons to user
         | interfaces, because they felt it may be useful in the future,
         | not because someone told them it will happen. That code was
         | very different in quality, sometimes too abstract, sometimes a
         | big multi-screen function doing plenty of magic. All of it had
         | nothing to do with SOLID -- it was always a violation of
         | another principle: KISS. Keeping it simple, an engineering
         | variant of Ockham's razor, does not contradict or replace
         | SOLID, it complements it, defining the scope of application of
         | other principles. If your requirements are complex enough, you
         | may need an abstraction -- if you really need it, here are some
         | guidelines. That's it, now keep it simple.
        
           | jcelerier wrote:
           | > I have not seen support of changing underlying DB as a
           | business requirement yet (well, outside of frameworks and
           | platforms, of course).
           | 
           | ... and, you haven't ever seen a business requirement of what
           | used to be a "software" to become a framework / plateform
           | instead ? it happens all the time
        
             | ivan_gammel wrote:
             | Well, that - "any" software becoming a framework - does
             | happen, but not all the time. Why does this matter?
        
             | gregmac wrote:
             | I've seen bits of this, and I've seen it happen in code
             | bases that were built as "abstract" so its components could
             | re-used.
             | 
             | But I've never seen it happen in any way close to
             | resembling what the original architect thought would
             | happen, and as a result, all those abstractions and generic
             | implementations not only added time to the mainline
             | development, but in the end actually got in the way of the
             | abstraction that was needed.
        
         | youerbt wrote:
         | Yes, let's abstract the database is probably one of the most
         | mindlessly applied rituals in an enterprise software. And if
         | running on different databases is not a feature of your
         | application, it is mostly harmful practice.
         | 
         | If you can decide on a language, framework, critical libraries
         | etc. then you should be able to decide on a database. It's
         | probably more important than your application anyway.
        
           | matsemann wrote:
           | Back before devops, containers, postgres etc were mainstream
           | (so not more than ~6 years ago in my industry), soo many were
           | running oracle dbs. And everyone shared the same instance,
           | and it wasn't exactly trivial to get a new personal up and
           | running (licensing, probably required a DBAs help). So then
           | using hsqldb or something else lightweight were golden for
           | local development or integration tests. So abstracting the DB
           | was the default, and absolutely needed.
        
             | no-s wrote:
             | >>using hsqldb or something else lightweight were golden
             | for local development or integration tests. So abstracting
             | the DB was the default, and absolutely needed.
             | 
             | This also improves efficiency in operations, not
             | necessarily development. If you used a library/framework
             | for database access anyway, it's not an extra expense.
             | There's ultimately a portability concern even if
             | "vendoring", it only imposes a cut-out to permit control of
             | necessary change).
             | 
             | After a few unpleasant experiences I endlessly advocated we
             | should always use an interface to access populated data
             | objects and not interact with the database directly, not
             | even running queries directly but always using at least
             | lightweight IOC. I also advocated for testing where known
             | result sets were fed through mock objects. After all, saved
             | result sets could also be used to test/diagnose the
             | database independently after schema/data changes. My
             | experience predates a lot of ORM and framework responses.
             | 
             | Unfortunately later frameworks (intended to abstract these
             | concerns) became ends in themselves, rather than a means to
             | an end. These were used to satiate "enterprise-y" concerns
             | (sacrifices to the enterprise gods). If you could afford to
             | deploy operational Oracle, you would't necessarily flinch
             | at the cost of the extra (often pointless) layers of
             | abstraction.
        
             | ivan_gammel wrote:
             | What platform are you talking about? In Java world JDBC
             | existed from early days and was enough if you stick to
             | standard SQL, in tests you may have needed only to switch
             | driver classpath and connection string. ORMs existed at
             | least since 2003-2004 (early versions of Hibernate).
             | 
             | At the same time, substituting Oracle with a lightweight DB
             | in an environment where full-time DBA was developing a
             | database with loads of stored procedures and DB-specific
             | stuff wasn't something really feasible - no abstraction
             | layer would solve that.
        
         | edoceo wrote:
         | Yea, 20+ years on PostgreSQL. Why would I change my DB away
         | from the perfect one?
        
           | TimTheTinker wrote:
           | One never knows what the future may bring.
           | 
           | Long-lived OSS software is a relatively stable bet, but the
           | point still stands.
        
             | rootsofallevil wrote:
             | >One never knows what the future may bring.
             | 
             | Exactly, so make the changes when you need them, otherwise
             | you are relying on hitting the abstraction lottery
        
             | jacques_chester wrote:
             | Maintaining database abstraction layers and the like is a
             | real option used to hedge the risk of the holdup problem.
             | But like any real option, it has a carrying cost. That
             | means it's an economic question, there is no purely
             | technical rule that can give you a robust answer to whether
             | or not to abstract something away.
             | 
             | I feel the industry has a long hangover from the 80s and
             | 90s in terms of the holdup problem. Oracle, basically,
             | created a massive externality of anxiety about vendor
             | lockin that continues to impose drag to this day.
        
             | pydry wrote:
             | Decoupling your data model from postgres because you
             | "might" need to swap database is a bet I've seen taken many
             | times but it's never one I've seen paid off.
             | 
             | This is a clear example of where YAGNI applies, I think.
             | 
             | Extra work plus extra boilerplate to maintain. No payoff.
             | 
             | That said, extra code + boilerplate is a great way to treat
             | riskier software.
        
           | rzzzt wrote:
           | Self-hosted, low volume or demo installations can benefit
           | from a lightweight database (that is also in-process, so the
           | user does not have to install and maintain it) like SQLite,
           | H2 or Derby.
        
           | majkinetor wrote:
           | IME, because client asked for the different one for whatever
           | reason and is willing to pay for higher development cost.
           | 
           | You can argue about that just the same as when client needs a
           | feature X.
           | 
           | In my case, I was very often getting away with YAGNI or 'how
           | about we implement nightly sync from pg to oracle' but not
           | always.
        
           | paulryanrogers wrote:
           | Also love Pg and using flavor specific features where they
           | deliver tangible value. That said, if a team maintains
           | multiple flavors often then an abstraction can improve
           | quality of life.
        
         | silentbugs wrote:
         | Exactly.
         | 
         | DRY and YAGNI are two basic concepts I've always tried to work
         | with. Do I need this piece of code more than once? Should be
         | extracted/created in a separate place. Would I need this in
         | other projects? Library.
         | 
         | YAGNI is most likely directly conflicting with most of what's
         | mentioned, if the developer simply asks themselves: do I really
         | need this? Will this project benefit from having separate
         | layers of abstraction for things that are most likely never
         | going to change?
         | 
         | I always think twice before writing a single line of code, with
         | the main point of focus being if future me (or anyone who reads
         | that piece of code) will be able to understand and change
         | things, if needed.
        
           | jehlakj wrote:
           | DRY seems to be one of those principles too many people take
           | literally. Especially among junior devs from my observation
           | (and experience).
           | 
           | Just because there is a block of code that's being used in
           | two different places doesn't mean it should always be
           | abstracted out. There's a subtle yet mindful consideration
           | whether these two consumers are under the same domain. Or if
           | it should exist as a utility function across all domains. And
           | if that's the case, changing the code to make it generic,
           | simple and without side effects is ideal.
           | 
           | I've seen too many of these mindless DRY patterns over time,
           | and they eventually end up with Boolean flags to check which
           | domain it's being used in as the codebase becomes larger.
        
             | gfody wrote:
             | DRY should really be DRYUTETNRYDTP - dont repeat yourself
             | unless the effort to not repeat yourself defeats the
             | purpose
             | 
             | I also propose LAWYD - look at what you've done, a
             | mandatory moment of honest reflection after drying out some
             | code where you compare it to the original and decide if
             | you've really improved the situation
        
               | sixothree wrote:
               | I honestly thought that was one of the principles of DRY.
               | Maybe I've been wrong this whole time. Will it be faster,
               | safer, more maintainable if I just leave this code in
               | here twice? At what point does that equation change?
        
               | fuzzy2 wrote:
               | Sure it is. However, teachers/mentors may be just a tad
               | to dogmatic and pupils/juniors may just be a tad to
               | inexperienced to properly grok when to do what.
               | 
               | And it sure is easier to just not argue about it and
               | abstract everything. The real pain only comes later,
               | after all.
        
             | sixothree wrote:
             | Just this week I came across a set of 20+ controls in a
             | form. Every control downloaded some version of a file from
             | "report". Not once was there any shared code behind any of
             | these controls. Because different people over time touched
             | this code, not all of the functions were in the same place.
             | And those that were each had slightly different nuances.
             | 
             | Without DRY, this would be a perfectly acceptable practice.
             | DRY gives me something I have in my head when I see this
             | and refactor into something that is manageable. DRY gives
             | me something I can point to and say "please for the love of
             | god don't perpetuate this folly".
        
             | h3mb3 wrote:
             | I think a good rule of thumb is to never _prematurely_
             | apply DRY in source code BUT always try to aim for it when
             | it comes to data and configuration unless there 's a
             | special need.
        
         | no-s wrote:
         | the most important take-away from Agile is YAGNI - "you aint
         | gonna need it."
         | 
         | That's what I mean when I say simple. Don't obscure your code
         | with unfalsifiable assumptions reified, as related in the
         | typical obscure vernacular...
         | 
         | I have had to deal with RDBMS-swapping for fairly large and
         | transactionally intensive applications. I appreciated code that
         | embodied SOLID to the extent that it reduced the amount of code
         | to inspect and improved the quality of sizing up what needed to
         | be done. However it was much easier to "lift" up systems to the
         | new objective than "unravel" previous efforts to insure
         | portability without a defined and testable objective of
         | portability.
        
       | EasyTiger_ wrote:
       | My god what awful clickbait
        
       | vsareto wrote:
       | SRP is vague because it really depends on how your team prefers
       | to draw boundaries around the responsibilities. Some places
       | choose to have meatier responsibilities (an entire ETL pipeline
       | as one class), or prefer smaller responsibilities (same ETL
       | pipeline, but each stage of it is a separate class). Both of
       | these could use roughly the same amount of code, whether it's
       | implemented in 1 class or across 3 classes. Some of this is
       | driven by how user stories and work is organized.
       | 
       | SRP never really recommended how large those responsibilities
       | should be, but that's almost impossible to do given how widely
       | programming is applied (especially for a quick acronym parroted
       | in interviews). Had it tried to specify this size, we would
       | likely be dealing with "OLID" instead of "SOLID", since it would
       | be much easier to criticize.
       | 
       | It comes down to how many tabs you want opened in your IDE and
       | how good you are at moving between them and keeping the object
       | relationships in your head. If I have a god object to handle one
       | responsibility, I only have to look at one file, but it might be
       | thousands of lines long. Or I can reduce that down to tens of
       | files open with hundreds of lines each. Or it could be hundreds
       | of files with only around ten lines each. The god object allows
       | me to mostly forget about any object relationships or
       | abstractions. But I pay some price by having to learn the entire
       | god object before being able to sensibly work on it.
       | 
       | If I have some object inheritance like:
       | 
       | IPipeline -> AbstractPipeline -> SqlPipeline -> PostgresPipeline,
       | SQLServerPipeline, MySqlPipeline
       | 
       | IPipeline -> AbstractPipeline -> NoSqlPipeline -> MongoPipeline,
       | CosmosDBPipeline
       | 
       | (every architect dreams of this)
       | 
       | It's easier to change things per database without affecting the
       | others. Since each of these products change independently of one
       | another (Mongo doesn't consult Microsoft about SQL Server), it
       | makes more sense to do it this way as opposed to having a god
       | object when you know you must support these other databases.
       | 
       | On the other hand, if you know you will only ever use Postgres,
       | IPipeline, AbstractPipeline, SqlPipeline/NoSqlPipeline become
       | _mostly_ useless abstractions.
       | 
       | This also varies greatly whether you're trying to build building
       | blocks for other developers or just trying to ship a product.
       | Principles aren't always going to apply to both of those, and SRP
       | might be one of them. It wouldn't hurt to add "depending on how
       | your team defines responsibilities" at the end of SRP.
        
       | wasnthere wrote:
       | The article, imho, reads like the main goal of the author is to
       | prove he has a bigger dick than Uncle Bob's...
       | 
       | sadly, he fails miserably at it.
        
       | andy_ppp wrote:
       | The tech community constantly give their opinions as truth (the
       | article is an example of this). I wonder if YC could be a
       | position to do some quantitative research about what works,
       | especially as companies evolve their codebase and if that even
       | matters as much as we software engineers feel it should.
        
       | makach wrote:
       | The SOLID principles are associated with model driven
       | development, and that paradigm. These principles have helped me
       | in many ways improving code, and explaining why it needs to be
       | that way. It makes it easy for me to bring consistency to the
       | code base and bring new people into the project.
       | 
       | There is definitively room for new models and new paradigms when
       | writing code, and I welcome that. The click-bait article is just
       | that... click-bait. It silly and reminds me of the "climb that
       | tree" quote.
       | 
       | Consistency has always been a big deal for me. Different code-
       | bases needs different principles, standards and models. If you
       | are consistently wrong you can consistently fix your issues.
       | 
       | My experience is that if you are inconsistent, you usually will
       | end up with the dreadful spaghetti of things.
        
       | fuzzy2 wrote:
       | The only way someone could arrive at the conclusion that "write
       | simple code" is superior to any clean code principles is when
       | they have already "absorbed" and perhaps transcended these
       | principles. What does this tell us? It takes a mature developer.
       | 
       | You cannot tell a junior developer to just "write simple code"
       | and then expect any kind of sensible result. But then, you also
       | cannot tell them to write SOLID code. Indeed, you need to teach
       | the basics first: How to understand the requirements. How to
       | write _any_ code that fulfills them. How to write _legible_ code
       | that fulfills them. How to write _good_ code that fulfills them.
       | 
       | If you dump these principles onto developers that have not yet
       | learned the proper developer mindset, the outcome will most
       | likely be disaster. It is very hard to repair the damage.
       | 
       | Only after a developer can actually write good code can you teach
       | them how to take it to the next level. How some libraries are
       | just so _awesome_ to use without having to check the docs. How to
       | conceive code you'll look at in 6 month and think "damn, that's
       | some shit".
       | 
       | Why every single clean code principle is a recipe for disaster -
       | if told to juniors.
        
       | mekoka wrote:
       | The problem with SOLID is not SOLID, it's the fetishism that
       | sometimes surrounds it. I get it, rules are attractive as they
       | make everything easier. You put on blinders and follow the track.
       | But the fact that there's so much disagreement about how to best
       | build software, with valid arguments in all sides of the multiple
       | spectra, should hint at its truer nature as a craft (some would
       | say a philosophy, but I haven't yet reached that level of
       | enlightenment) rather than an exact discipline. Isn't it time to
       | perhaps get comfortable with the idea that there are no perfect
       | solutions, only trade-offs and that the right answer is most
       | likely "it depends"? I understand that some people feel
       | protective of their investment at internalizing the various
       | "principles", but learning them doesn't give anyone a license not
       | to think and to mindlessly apply them. And yes, SOLID is mostly
       | useful in its essence, but over time parts of it have caused
       | confusion and have not always aged well
       | (https://codeblog.jonskeet.uk/2013/03/15/the-open-closed-
       | prin...).
       | 
       | At a more personal level, the DIP section of this article hit
       | close for me, as I've (again) lived the described scenarios a
       | mere week ago. Sometimes `main()` is all you need and all you'll
       | ever need. The extra tooling is also debt. If you choose to incur
       | it, it _must_ palliate to an actual problem that you actually
       | have, else YAGNI.
        
       | sorokod wrote:
       | I think that the problem with SOLID principles was the use all
       | capital letters and the word principles instead of guidance.
       | 
       | Now that that original sin has been committed, it is super easy
       | to take shots at it.
        
       | recursivedoubts wrote:
       | The Single Responsibility Principle (and the related Separation
       | of Concerns Principle) have been particularly damaging in the
       | context of UI development.
       | 
       | They are reasonable principles and _often do_ apply, but there
       | are times when the concept of Locality of Behaviour is better for
       | overall system simplicity and stability:
       | 
       | https://htmx.org/essays/locality-of-behaviour/
        
       | zoomablemind wrote:
       | As the author described it in the preface, the topic was rather
       | meant to be engaging and entertaining for the occasion. So let's
       | not confuse this with a true debate over SOLID.
       | 
       | > _"... So what would I do instead? I thought there might be a
       | one-to-one correspondence for each of the SOLID principles and
       | patterns, since there is nothing inherently bad or wrong with any
       | of them, but as the saying goes, "If I were going to Dublin, I
       | wouldn't start from here."  "_
       | 
       | As anything, SOLID is a guideline, not a dogma. The purpose is to
       | encourage developers to approach the design of their code
       | mindfully.
       | 
       | Personally, in my memory the advent of SOLID principles coincided
       | with widespread promotion of better approaches to testing. These
       | principles indeed helped design for better testing. And better
       | testing provided more freedom for changes going forward.
       | 
       | If another set of priciples help one's projects better attain the
       | set goals, well, sure, embrace these by any means.
        
       | lifeisstillgood wrote:
       | Many years ago I read an article about Boeing's chief engineer -
       | he apparently had the "whole aircraft in his head" - it as a
       | schematic but as a series of deeply understood components the
       | fitted together (from Bernoulli's principle to hydraulic pressure
       | equations and the weight of fuel distribution as tanks empty. The
       | whole enchilada).
       | 
       | There is nothing particularly magical - one assumes that the top
       | 10% of any MIT graduating class could be taught to do the same
       | over a decade or less.
       | 
       | But I am guessing that it needed a organisational effort to
       | ensure the bits added never got beyond one person, and that he
       | was comfortable saying "stop".
       | 
       | Every railway and new or refurbished bridge needs to be signed
       | off by a railway engineer - who can and should refuse if it is
       | sub-standard. Keeping our software under the same degree of
       | control will need similar levels of handing the keys of the
       | kingdom over to a small number of devleads. And ensuring they do
       | not encounter unmanageable conflicts of interest.
        
       | skohan wrote:
       | I always took SOLID with a grain of salt, because they clearly
       | started with the acronym and worked backwards to the constituent
       | parts.
       | 
       | Is Liskov's substitution principal really one of the 5 most
       | important pillars of software design, or is it just the best one
       | that starts with an L?
        
         | jdlshore wrote:
         | One of Bob's anecdotes is that it took him several years after
         | he started teaching the SOLID principles to realize that they
         | could be rearranged to form the word "solid." So, no, I don't
         | think he started with the acronym.
        
         | Transfinity wrote:
         | The author mentions in the post, and I think I agree, that
         | Liskov substitution is the most "principle-like" of the
         | principles in that it's pretty much always applicable. If the
         | child object can't be substituted for he parent object, then
         | you haven't really extended it, you've just made another
         | unrelated class that happens to share some code. Inheritance is
         | probably the wrong pattern here, and if you call what you've
         | done inheritance you're going to confuse the person who next
         | touches this code.
        
           | skohan wrote:
           | I'm not arguing that it's not a real thing, but how often
           | does the LSP come up in your day to day life as a programmer?
           | I have not thought about this once, and I do not think there
           | is a problem this has solved for me in 10 years.
        
             | mypalmike wrote:
             | It's not always easy to discover interface commonality. But
             | when you do and it's a good abstraction, there are
             | definitely benefits over scattering ifs and switches
             | throughout your code.
        
       | nautilus12 wrote:
       | Why everything you know about software engineering is WRONG, and
       | this one WEIRD trick you can do to get ahead of the pack.
        
       | njharman wrote:
       | I was hopeful, because I tend to think most "universal"
       | principals are 80% working around problems/limitations/design
       | choices of, at the time, mainstream programming languages (C,
       | C++, Java). Which don't apply to (some) other languages (which
       | have different limitations of their own).
       | 
       | But, I don't think this author gets it (what the SOLID elements
       | actually are, nor how they are moot when using languages that
       | solve the same issues they are solving).
        
       | j_san wrote:
       | Uncle Bob has actually written a blog post about Dan's
       | presentation: http://blog.cleancoder.com/uncle-
       | bob/2020/10/18/Solid-Releva...
        
       | smusamashah wrote:
       | Link to slides https://speakerdeck.com/tastapod/why-every-
       | element-of-solid-...
        
       | HALtheWise wrote:
       | One of the most useful pieces of programming advice I have been
       | given is to make an abstraction on the third time you solve a
       | given problem. It encourages making abstractions for common
       | tasks, but also acknowledges that in a large codebase the wrong
       | abstraction is an order of magnitude more expensive than not
       | having an abstraction at all, and it's unlikely that you have
       | enough information to make the right abstraction the very first
       | time you are looking at a problem.
       | 
       | As with all things, there is a cost (slightly more code
       | duplication for rarely-solved problems) but also huge benefits in
       | having really solid abstractions built from lots of experience.
        
       | nottrobin wrote:
       | I think a couple of the criticisms are valid - mostly the
       | criticism of dependency inversion.
       | 
       | But overall I think the article (if we were to take it seriously)
       | basically represents a nihilistic point of view that says
       | "there's no point trying to have principles - code is either
       | subjectively good or it isn't".
       | 
       | I'm as sympathetic to heterodox criticism as anyone, but this
       | desire to throw away any received wisdom at all definitely seems
       | misguided to me.
       | 
       | (For what it's worth, I think the single responsibility principle
       | and the open closed principle are particularly valuable. I'm open
       | to persuasion on the others)
        
       | he0001 wrote:
       | SOLID isn't very good, but most of the alternatives are worse. I
       | mean if the author thinks the SOLID principles are vague, but
       | then go on saying "it should fit in my head" seems not like a
       | good replacement. That is also very vague and arbitrary. Perhaps
       | the author should combine them and it gets somewhat less vague.
        
       | hertzrat wrote:
       | He says the principles are wrong and says instead to just write
       | simple code, but doesn't offer suggestions for how to keep it
       | simple. As if simple code is easy to write. There's nothing more
       | simple than a giant list of global variables. SOLID is an attempt
       | to keep the code as simple as possible as it grows in size and
       | complexity. It's not perfect but anything is lousy if you misuse
       | or overdo it
        
       | Syzygies wrote:
       | SOLID acronym:
       | 
       | https://en.wikipedia.org/wiki/SOLID
       | 
       | As a pure mathematician, whenever I find myself reading an
       | acronym-heavy paper I know I'm in the wrong part of town.
        
       ___________________________________________________________________
       (page generated 2021-03-21 23:00 UTC)