Solid Programming - No Thanks

Поделиться
HTML-код
  • Опубликовано: 24 апр 2024
  • Recorded live on twitch, GET IN
    Article
    www.freecodecamp.org/news/sol...
    By: Yiğit Kemal Erinç | x.com/erinccodes
    My Stream
    / theprimeagen
    Best Way To Support Me
    Become a backend engineer. Its my favorite site
    boot.dev/?promo=PRIMEYT
    This is also the best way to support me is to support yourself becoming a better backend engineer.
    MY MAIN YT CHANNEL: Has well edited engineering videos
    / theprimeagen
    Discord
    / discord
    Have something for me to read or react to?: / theprimeagenreact
    Kinesis Advantage 360: bit.ly/Prime-Kinesis
    Hey I am sponsored by Turso, an edge database. I think they are pretty neet. Give them a try for free and if you want you can get a decent amount off (the free tier is the best (better than planetscale or any other))
    turso.tech/deeznuts
  • НаукаНаука

Комментарии • 806

  • @JayHiza
    @JayHiza Месяц назад +384

    "Program until you NEED abstraction" is on point. I was having this debate at work yesterday, and I said something very similar. Unless you're a savant, who can architect a program entirely in your mind, the most logical thing to do is to write code until you realize that abstraction would be beneficial, implement it, and continue writing code. To do otherwise is just wasting time in my opinion.

    • @zeez7777
      @zeez7777 Месяц назад +19

      Yes, but the important part here is to actually be aware of that and introduce the abstraction and refactor.
      But you're right.

    • @somniad
      @somniad Месяц назад +14

      Big important problem with this statement: the savant also can't architect it in their mind. Nobody is that good. It's not a little bit out of normal reach to the point where you could get it if you had experience and thought reaaaaally hard about it. Requirements change.

    • @AndrewBrownK
      @AndrewBrownK Месяц назад +10

      spoiler: nobody is that savant

    • @defeqel6537
      @defeqel6537 Месяц назад +1

      This is a good starting point, another is to abstract platform specifics. Abstraction can just be a C/C++ declaration too though.

    • @Linkario86
      @Linkario86 Месяц назад +10

      ​​@@zeez7777this is where it starts to crumble. It's tricky though. If you abstract everything beforehand, there is a big tendency to over engineering. If you don't do it there is suddenly no time and money for refactoring, and "it works" so why bother? But it works bad, slow, hard to read and maintain and it's crumbling.

  • @PaulPhilp-pe5oe
    @PaulPhilp-pe5oe Месяц назад +269

    This topic always reminds me of the famous Miles Davis quote: "Learn the score, then throw it away" is a quote often attributed to jazz musician Miles Davis. The quote emphasizes the importance of mastering the fundamentals and techniques of one's craft, but then being able to move beyond them and improvise freely.

    • @jeffreyjdesir
      @jeffreyjdesir Месяц назад +6

      😮 Great knowledge from my legend that I didn't even know. Thank you! Competent practitioners of most advanced skills tend to realize this one way or another.

    • @banatibor83
      @banatibor83 Месяц назад

      True! But there are much more software developers out there who are not on this level.

    • @filipg4
      @filipg4 Месяц назад +8

      My brother in Christ, SOLID is not fundamentals, it's literally the highest level possible trash knowledge that is as far from fundamentals as possible.

    • @PaulPhilp-pe5oe
      @PaulPhilp-pe5oe Месяц назад

      @@banatibor83 True. Nothing I've ever written is the 'Sketches of Spain' of software, so I keep learning, mastering and owning what I can.

    • @LusidDreaming
      @LusidDreaming Месяц назад +1

      It should be noted that there are also just flat out bad scores that no creative freedom can fix (without entirely replacing the original)

  • @angelcaru
    @angelcaru Месяц назад +321

    liquid programming

  • @rampantporcupineandfriends3793
    @rampantporcupineandfriends3793 Месяц назад +83

    This reminds me of the book A Philosophy of Software Design. It states that you should only create an abstraction if it reduces the complexity of the system.
    It calls abstractions that are a net drain on the simplicity of the system "shallow" and ones that are a net gain as "deep".
    The book also acknowledges that this means that one piece of code may have to do multiple types of tasks.

    • @Dogo.R
      @Dogo.R Месяц назад +4

      Define "complexity".

    • @rampantporcupineandfriends3793
      @rampantporcupineandfriends3793 Месяц назад +23

      @@Dogo.R The books definition of complexity is this. "Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system. Complexity can take many forms. For example, it might be hard to understand how a piece of code works; it might take a lot of effort to implement a small improvement, or it might not be clear which parts of the system must be modified to make the improvement; it might be difficult to fix one bug without introducing another. If a software system is hard to understand and modify, then it is complicated; if it is easy to understand and modify, then it is simple." -John Ousterhout
      The book starts by introducing this definition and then goes on to describe how to recognize this complexity and how to mitigate it. I've been using its principles in my current project, and I've been really enjoying it. :) I feel like it helps make up for my lack of intuition as a relatively new programmer.
      I shared the book here because I feel like a lot of what Prime says lines up with the book pretty well. If you like Primes software design philosophy maybe check it out for a more detailed and structured take on things.
      This is a talk where he introduces his book ruclips.net/video/bmSAYlu0NcY/видео.html

    • @monad_tcp
      @monad_tcp Месяц назад +3

      Essential complexity versus accidental complexity.

    • @rampantporcupineandfriends3793
      @rampantporcupineandfriends3793 Месяц назад +5

      @@monad_tcp Yes, at the end of the day every system you create will have complexity. But you should aim to keep that complexity close to the minimum needed to get the job done.
      When looking at Primes design principles through this lens you can interpret his idea that you should start with the "concrete" of a solution and discover the abstraction later as an attempt to limit complexity.
      Similar ideas are explored in the book which spends a lot of time talking about how to spot bad abstractions and how to find good ones. However, there are some significant differences between the books philosophy and what I understand of Primes philosophy. For instance, the book is not as opposed to starting your design process by coming up with an abstraction.

    • @swapode
      @swapode Месяц назад +3

      ​@@rampantporcupineandfriends3793 You start your design process with something you know, which is something Prime seems to be struggling with in general, as seen by his arguments against TDD (assuming that you need to know pretty much the entire project before you can write meaningful tests, which is just an absurd understanding).
      Sometimes you obviously know that certain abstractions are central to your project.

  • @DarylMetzler
    @DarylMetzler Месяц назад +19

    I think a big ding against SOLID is that theyre called "principles". Theres only one principle, and it is "manage complexity".
    Maybe these should be called "perspectives" (the name Lens or Optic already got stolen by the FP folks)
    And the perspectives are best understood by the outcome of violating them:
    - SRP: no god classes
    - O/C: avoid fragile base classes
    - Liskov: dont have your subtype do something completely different
    - ISP: no thousand method interfaces
    - DI: dont couple to dependencies.
    Ive seen time and again that avoiding this (decent, vague) advice, the worse and more convoluted the code gets.

  • @AndrewBrownK
    @AndrewBrownK Месяц назад +74

    A lot of these seem to convey an emotion of "if you ever have to refactor this, then you made a mistake. You should predict and/or accommodate the future perfectly"
    I'd much rather see principles around the idea that "you cannot avoid refactoring, the only question is how to make it as painless (or dare I say easy) as possible"
    and a lot of that difference is diametrically opposed when it comes to the guidance on inheritance

    • @deloftie3619
      @deloftie3619 Месяц назад +9

      All of the SOLID principles are to facilitate easy and painless refactoring. Most modern devs don't care that much about maintenance and refactoring though (they just made a pile of mud and then move project and/or job and start again) which is why you get videos like this that are saying don't bother with SOLID principles.

    • @Chisegh
      @Chisegh Месяц назад +5

      @@deloftie3619 In my experience solid principles make refactoring harder because more often than not you build the wrong abstraction, resulting in first having to undo the abstraction and then building the right one. In a perfect world where we can perfectly predict the future solid makes sense. Reality is very different though.

    • @deloftie3619
      @deloftie3619 Месяц назад +5

      @@Chisegh I don't follow what you mean by "undo" the abstraction. If you follow SOLID principles you never need to change your existing code as your product changes or matures. You are only ever adding new code
      It is the exact opposite of having to predict the future, which is what you have to do with procedural code because you have to either know that function X does everything it will ever do or you have to add new functionality to the existing function X as you develop.
      This is because functions are tightly bound (it is difficult to say to your exist code "Stop using this function, use this one instead") so existing functions are constantly changing and growing, which massively increase the complexity of the functions and the risk that bugs will be introduced.
      I've worked on so many software products where after a few years every function is a collection of if statements for all the new functionality that has been piled upon the function over the years

    • @AndrewBrownK
      @AndrewBrownK Месяц назад +2

      @@deloftie3619 to put it simply, I disagree.
      "it is difficult to say to your code, stop using this function, use this one instead"
      I only find that to be difficult when the functions entail large amounts of state, which is you guessed it, from OOP
      The worst nightmare refactors I've ever seen have all involved unnecessary class hierarchies and the inability to fix problems without causing problems in new places, or ripping everything inside out to procedural again.
      When procedural code just does what it says right in front of you, instead of reaching through so many abstractions, it is easy to refactor by comparison. Especially with a dash of functional programming to minimize use of state overall.

    • @deloftie3619
      @deloftie3619 Месяц назад +2

      @@AndrewBrownK "I only find that to be difficult when the functions entail large amounts of state, which is you guessed it, from OOP"
      Well its not state I'm worried about in this situation, its binding.
      If you have a function that is called in a 100 places in your code and then you need to alter how some of those places behave you have a tough time about it. I've seen so many large functions with multiple paths that have been added over the years to account for new functionality that has had to be brute forced into the existing function.
      And don't get me started about having to slowly increase the parameter size of a function as the function becomes responsible for more and more.
      "involved unnecessary class hierarchies"
      Well sure, the "I" in SOLID is essentially don't have class hierarchies that can break your code.
      "When procedural code just does what it says right in front of you"
      It does, and it is perfectly fine for small projects that won't change much. I've nothing against procedural code, I write C and procedural Python all the time.
      But it is a nightmare to have to maintain procedural code in any large system (again binding) that grows and matures as the business requirements evolve. Which is exactly what OOP was invented for.

  • @roberthoople
    @roberthoople Месяц назад +240

    The whole OOP vs Functional thing just didn't make any sense to me when I first fell down the rabbit hole, but after hearing enough of it I've come to realize it's a false equivalency. Basically it's just click bait to get views on articles and videos.
    I just write and organize my code for the best balance between readability and optimization, which turns out to just basically be Functional OOP.
    Although, I do get people's beef with inheritance, and when I first learned OOP I way overused it. Since then, I only use inheritance when it's the best way to solve a problem, which is very rare, but very powerful in those rare situations. Yet it's another thing people want to fight about and claim a position of being anti or pro on... So it's no wonder all the software on my PC and phone are getting worse and worse with every new release.

    • @samgraham182
      @samgraham182 Месяц назад +19

      Thank you! I had to maintain an overused inheritance project in Ruby once, it was horrible. Everything was hidden. I had to read code for hours and hours just to grok anything. Then I got reassigned to another issue that was higher priority in another project. RIP whatever issue that was, it's been a decade and I still remember the frustration.

    • @cloudboogie
      @cloudboogie Месяц назад +4

      @@samgraham182 Oh man, that's classic ruby. I don't miss working with it: bloated rails, annoying metaprogramming in the least expected places, "rockstar developer with god complex" coleagues, cultish "the rails way", hours long discussions about code style, etc.To hell with it

    • @hextech687
      @hextech687 Месяц назад +11

      I dont really study programming too much (Im more of a make things work kinda guy, trying to change this). Im very much a vibes based programmer. The realizations you have come to are very close to what I have been leaning towards when building things. Locality of behavior is really important for me. The only reasons I break it is if the same logic is needed elsewhere and should be aligned. When I have to read 4 different files to figure out what one functions does I get very angry and want throw laptop.

    • @blarghblargh
      @blarghblargh Месяц назад +6

      when people do the "functional vs oop" thing, they are raising the problems with oop, then trying to come up with something to contrast it with. but structured programming wasn't en vogue anymore when these comparisons first cropped up, and functional programming seemed like the new hotness (despite being invented some time in the 50s through 70s). so fp got saddled with oop's baggage, being nominated as the next big island of thought to jump to.
      turns out neither is a good dogma, and they're just different ways to add abstraction to a program. abstraction can be valuable, but it comes with a set of ongoing costs. it should be deployed in hesitation, when it produces significant value in return, when you have a provable, reasonably sizable, and current burden that it resolves.

    • @Asto508
      @Asto508 Месяц назад +3

      I honestly don't believe that you only use inheritance very rarely. Every time you use an interface/abstract class, you are using inheritance. The Strategy pattern is one of the most useful applications of inheritance and it's rarely not useful at all.
      It enables you to swap out implementations in parts of your code that don't ripple through other areas and that alone makes it such a wonderful tool for long term maintenance.

  • @mgoonga
    @mgoonga 25 дней назад +6

    I work in a game engine project which was initially designed with SOLID in mind. At the beginning a lot of those abstractions just seemed unnecessary. However, as the project grows and becomes really big it starts to pay off! It is just a pleasure to work with such a project. For example the basic windowing system was changed two times already from QT to SDL and than to GLFW. Nobody new this would be so at the start. However, as the windowing code is abstracted in a separate static library and communicates with core through small interfaces like IInputObserver which calls abstract functions like OnKeyDown/Up etc. changing the whole windowing system takes a few hours and produces no bugs AT ALL! This is just a simple example. The whole project is like that. A big project created with SOLID in mind is a pure gem!

  • @SKULDROPR
    @SKULDROPR Месяц назад +23

    Prime is so correct at the end there. (Almost) Always write out concrete versions of your classes, then go back and abstract them. It took me 10 years to learn this (I am a slow learner). This way you know what parts are repeated a lot, you can then tease them out and abstract them as needed. Your overall structure will make more sense from the first abstraction attempt, instead of doing it as you go along, where you can't predict your ass from your elbow.

    • @sasieightynine
      @sasieightynine 15 дней назад

      Could you please explaining this in more detail or refer an article that does?

    • @SKULDROPR
      @SKULDROPR 15 дней назад

      @@sasieightynine I didn't read it anywhere unfortunately. It is just a pattern that I learned to recognize after making the same mistakes over many years.
      I suppose an easy way of putting it is something like this:
      Start by writing your program in the dumbest, most straight-forward way possible. Iterate upon this and try to tease out any repeated parts and abstract them, but only abstract them if you NEED to. Don't just abstract things for 'the future', it doesn't exist. You are in for a world of pain if you go down this path.
      Instead, focus on the problem at hand, and possibly the most predictable outcomes if you have the resources to spare.

  • @ReneHartmann
    @ReneHartmann Месяц назад +86

    The problem with SOLID is that one needs a lot of experience to understand and interpet it in a way that's actually useful, but if you have this experience you don't really need SOLID.

    • @damdoumibou3ajaja338
      @damdoumibou3ajaja338 Месяц назад +16

      This is not a problem. This is true everywhere in software. Experience is key to everything. Principles written in books are worthless without it. That's why in his Book "Clean code". Robert C. Martin goes into extreme lengths to explain that we need to think hard about and experiment long with the SOLID principles he is proposing. Nowadays, they are just catchphrases used in interviews or in Pull Requests to make an argument...

    • @Dom-zy1qy
      @Dom-zy1qy Месяц назад +4

      I kind of agree to a certain degree. Single responsibility and dependency inversion are pretty easy to understand and don't require much experience to benefit from.

    • @evancombs5159
      @evancombs5159 Месяц назад +3

      ​@@Dom-zy1qy of single responsibility is so simple why do so many people, including the article in this video get it wrong?

    • @Justin73791
      @Justin73791 Месяц назад

      @@damdoumibou3ajaja338 Robert C. Martin recommends principles that can not be implemented well regardless how much experience you have. 12 lines per function is just the wrong foundation no matter how you apply it.

    • @owenreynolds8718
      @owenreynolds8718 Месяц назад +2

      I think of it as "SOLID adds no marginal value over what you already should learn". For example, that part where the article says "logic and a database in the same class violates SRP". Well, basic OOP already says to de-couple by slapping an abstract interface over the DB (if the project is large, mostly in the final state, and expects to swap out different databases). Thinking of that as fixing an SRP violation adds nothing useful. Likewise, learning about public-interface/private-implementation is worse if it starts with "here's SRP, now we're going a learn a technique to avoid it".

  • @optimisticenigma283
    @optimisticenigma283 Месяц назад +186

    Jesus these bots be saying anything, gosh

    • @FRAMEDSKATEKREW69
      @FRAMEDSKATEKREW69 Месяц назад +31

      Googles priorities are in the wrong area 😔 they can shadow ban us for saying cursed words but can’t do that with bots 😂

    • @_BonsaiBen
      @_BonsaiBen Месяц назад +1

      Thumbnails ok tho 🤣😎

    • @electrolyteorb
      @electrolyteorb Месяц назад +1

      i am a BSD enthusiast

    • @penewoldahh
      @penewoldahh Месяц назад +5

      Jesus these bots be saying anything, gosh

    • @nikolaygruychev2504
      @nikolaygruychev2504 Месяц назад +1

      Jesus these bots be saying anything, gosh

  • @splitpierre
    @splitpierre Месяц назад +5

    Now THAT was SOLID video.
    Thanks Primeagen, I've been coding for about 10 years now, have read about SOLID before, but I've never fully agreed to all principles, I like going with the flow, just like you, start with concrete and hands dirty right on, then I take a sense on what actually needs to be abstracted and move on, thing is I thought I was a poorer programmer cause I don't fully agree with SOLID, this video gave me a lot more confidence as I honestly agree with practically all the arguments you brought.
    Being a good programmer is finding a balance in between strong principles and compromises, for the overall benefit around the application being built, our mental health, our future-self, project turn-over, quality, maintainability... a balance in speed and quality.

  • @grandeau3802
    @grandeau3802 Месяц назад +25

    There are basically three main (or meta) principles above all: KISS, DRY and No-Principle-is-Sacred.

    • @lifelover69
      @lifelover69 Месяц назад +4

      I generally agree, but I have worked on codebases that were a nightmare due to taking DRY literally :)

    • @georgehelyar
      @georgehelyar Месяц назад +4

      DRY has its own problems and can lead to tight coupling so there's a balance to find there as well.

    • @Nightwulf1269
      @Nightwulf1269 29 дней назад +4

      Fully agree to KISS. For me YAGNI is important too. But DRY is a difficult one. I generally won't repeat myself implementing the same logic in different places for the same types. But e.g. having the same string at different places doesn't bother me.

    • @cod3r1337
      @cod3r1337 29 дней назад +3

      I'd say YAGNI is way more important than DRY

    • @Nightwulf1269
      @Nightwulf1269 29 дней назад

      @@cod3r1337 for sure! I try to concentrate on KISS and YAGNI for my projects as well in my day job and private projects. And, what I in addition to that absolutely hate is when people are writing code in different languages in exactly the same way. E.g. writing a microservice in Go with hundrets of dependencies as they would in Node or Java. Go is all about KISS and that is absolutely ridiculous!

  • @XKCDism
    @XKCDism Месяц назад +5

    In my experience there are VERY few hard rules or principles one should follow, but a lot more guidelines.
    When I first started programing my OOP inheritance tree would make the Habsburgs blush. And then during the functional renaissance I tried to use it everywhere and it didn't work out. because a functional approach didn't fit the problems.
    Remember paradigms serve us to solve problems we don't serve paradigms, use the right tool for right problem and don't be dogmatic.
    Some problems are easily solved by inheritance, or composition/interfaces, or a functional approach.
    And experience and your personal discretion will help you make that right choice.

    • @jakubrogacz6829
      @jakubrogacz6829 Месяц назад

      Because all of them are solutions but also constraints on otherwise possibleto calculate code. Same as languages, ideal one would allow me to do anything, best up to declaring new keywords andsemantics if I need to while preventing me from usinng that on accident.

  • @markemerson98
    @markemerson98 Месяц назад +26

    locality of behaviour is a thing - if you have to reach outside to a wider radius from where your working then your cognitive load shoots out the park - so to speak - which seems counter intuitive of the principle that we want our code to be easier to read and understand

    • @deloftie3619
      @deloftie3619 Месяц назад +1

      This is the problem that we are stuck in this cycle of someone writes poor code and then someone else has to debug that code and find out why it isn't working. So everyone is constantly trying to understand what the hell the other person wrote. At this level having a procedural style "everything is happening in this one function" approach makes this easy.
      The problem with that is that you end up with a God awful mess at any abstraction higher than the specific bug or issue the programmer is trying to fix. These apps become balls of mud, where fixing specific problems is trivial but any larger change becomes almost impossible.
      So the cycle becomes an app that it is easy to fix what your buddy wrote a day ago but you have to throught he whole app out every few years and start again. Developers are so used to this cycle now that they see anything else as an impossible utopia

    • @raccoons_stole_my_account
      @raccoons_stole_my_account Месяц назад

      It's almost like people who created SOLID are in the business of consultancy aka never actually solving any problems because they need a next paycheck.
      Imperative programming is very easy and straightforward, once you know your problem and your program, you map one on the another.
      OOP is a cult where to solve a problem you need to travel to a shrine, pay a high priest a bribe, burn incence to the gods, hope for ten years and then interpret random events like proof it did actually work. Guess which one corporate parasites prefer.

  • @DeathSugar
    @DeathSugar Месяц назад +6

    Generally Liskov principle states "dont let the tail wag the dog", so hierarchy of classes should be in sane composition with each other. Rust traits are the good example of following the principle

  • @TECHN01200
    @TECHN01200 Месяц назад +11

    My gut feeling is have a tendency to under abstract because abstracting from a concrete state is easier than to make concrete from an abstracted state. It almost always is a one way road.

  • @steamer2k319
    @steamer2k319 Месяц назад +35

    Regarding pre-emptive abstraction:
    You
    Ain't
    Gonna
    Need
    It
    ...until you do... But most people only get ~5% of such predictions right--which leaves ~95% useless, brittle, noisy wasted-effort if allowed into the codebase. As Prime said, by the time you have proof that the abstraction is needed, it's easy to extract out exactly what's needed from your initial concrete implementation.

    • @Asto508
      @Asto508 Месяц назад +2

      I think those numbers are not really reflecting reality. Or maybe they do and I'm just biased. Depending on the domain and personal experience level, you can more or less safely predict future requirement changes and plan where abstractions might be needed and where not; and if you needed them in hindsight, you are glad that you prepared for it when there was plenty of time.

    • @steamer2k319
      @steamer2k319 Месяц назад

      @@Asto508
      The percentage definitely goes up with experience but it's also definitely low for juniors. One of the most significant ways seniors increase their odds is by simply making a lot fewer baseless assumptions/predictions.
      Software engineering is fairly easy to change on short notice, especially if you're familiar with e.g., JetBrains' refactoring tools and also if the code sticks to what's actually needed / in-use. What's hard to change on short notice is architecture/infrastructure so it is good to get that as right as you can up front.
      Still technology is complex enough that the first #83 features of a platform will align really closely with your requirements and then six weeks in you try feature #84 and find you got bit in the @$$ by it's marketing. Then you transition onto platform plan B because the large gap it has on feature #6 is actually easier to close even after re-implementing 🙄.

    • @defeqel6537
      @defeqel6537 Месяц назад

      @@Asto508 it also heavily depends on whether the code is contained or spread through the code base like cancer, and on the flip side (somewhat) if there is some "god class" thing going on

    • @Asto508
      @Asto508 Месяц назад

      @@defeqel6537 If the code has a high coupling, then that's just an example of doing it wrong and doesn't have much to do with pre-mature abstraction

    • @defeqel6537
      @defeqel6537 Месяц назад

      @@Asto508 I agree, but your abstraction may be the interface of the concrete implementation too. As long as there is a single piece of code handling the functionality, it is simple enough to extract a polymorphic abstraction later on.

  • @almicc
    @almicc Месяц назад +148

    I find it so annoying to have the question "hm, database isn't saving correctly, let me see what happened" and then I need to open PersistenceManager, DatabasePersistence, InvoicePersistence, and Invoice before I can start debugging. Thanks SOLID, very understandable! Worst part is you'll go right to Invoice and wonder where the database saving is, and so you need to search for Invoice and then you find InvoicePersistence and then you realize that there's no logic, it's an interface. Now you need to search for InvoicePersistence to find where it's implemented, then you find DatabasePersistence. And then turns out the bug isn't there, it's actually in some random file that calls invoicePersistence.save() and it turns out someone passed a FilePersistence to the wrong method somewhere else and so DatabasePersistence.save() was never actually getting called in the first place.

    • @jeffreybritton3338
      @jeffreybritton3338 Месяц назад +9

      Brilliant comment

    • @deloftie3619
      @deloftie3619 Месяц назад +29

      Yeah but you need to first ask yourself why are you doing any of that. It isn't anything to do with SOLID. You have separated your code base into small easy to test units and then didn't test any of them. If you have to instantiate the entire system to figure out where a bug is occurring then something has gone seriously wrong earlier in development.

    • @MaddieM4
      @MaddieM4 Месяц назад +5

      ​@@deloftie3619 You say that like `saveInvoice(invoice, database)` would be difficult to test.
      OOP tends to be hostile to tests in my experience, because intractably bundling data and functions makes it very difficult to validate that the right side effects happened to the right objects at the right time. That's not me arguing for FP, but a strict separation of data and logic is literally always healthy.
      I also, and I say this as a huge fan of testing, warn that your test suite will entrench any structural status quo that you test. This means that structural methodologies that cause large sprawling implementations (like OOP) will become calcified by your test suite. If you do try to refactor those abstractions later to be less cumbersome, you'll be glad the tests are keeping you safe, but they'll also double the effort of the project, and that's the trade-off.

    • @volvo340variomatic9
      @volvo340variomatic9 Месяц назад +6

      @@deloftie3619 In my experience, the nasty bugs are between layers (of abstraction). Unit tests are fine, but automated integration tests that cover multiple layers (e.g. from UI to database and back) can be gold.

    • @deloftie3619
      @deloftie3619 Месяц назад +2

      @@MaddieM4 "That's not me arguing for FP, but a strict separation of data and logic is literally always healthy. "
      The problem with that is that in large systems the code becomes tightly coupled data pipelines that are very hard to change. Imagine it like stringing together a whole load of Linux commands with pipes and then deciding a few years into production that actually the data format between two of the pipes needs to change.
      In OOP you are trying to, as much as possible, get rid of "data" as a separate concept to "behaviour". This obviously can be hard to achieve, particularly if engineers are used to thinking of systems as data pipelines with procedural functions manipulating a piece of data and then passing it on. But if you can crack it you find you can open up your system to be much easier to change and evolve. And all the SOLID principles are doing are showing how that can be achieved.

  • @jjurksztowicz
    @jjurksztowicz Месяц назад +20

    Inheritance is just one way to implement polymorphism. If you need polymorphism, then the complexity will accrue somewhere, in the class hierarchy, a switch statement, or elsewhere, you can't really avoid it.

    • @defeqel6537
      @defeqel6537 Месяц назад +1

      Clean Code actually has a decent section on this: "Data/Object Anti-symmetry"

  • @ssamani24
    @ssamani24 Месяц назад +4

    2:06 This is the main point. Using SOLID principles is great and all but if you’re implementing abstraction after abstraction where the readability, testability, and maintainability suffers, it means you need to scale back and refactor. But more often then not SOLID principles can help you achieve those goals but like many other aspects, developers can take things too far. It’s the old adage “if all you have is a hammer, everything looks like a nail” (and that’s how I feel about functional programming also)

  • @andrewshirley9240
    @andrewshirley9240 Месяц назад +10

    OOP is pretty bad when you build something under the philosophy of "I want this behavior to be standard everywhere" but then it turns out that behavior varies slightly and need independent implementations that you're trying to wildly tie together in dumb, complex ways. Procedural is bad when you say "I'm just going to build what needs to be done" and then you end up with a system where changing one flag means you need to track down 30 different places in code to add the same if statement in every independent implementation and everything burns down if you miss one. As prime says, it's all intuition, you're going to need to be able to make reasonable assumptions about how a particular problem is likely to evolve as it grows, and use the correct approach accordingly.

    • @Asto508
      @Asto508 Месяц назад +6

      Your first sentence is literally what OOP is not about.
      One thing that OOP tries to solve in an elegant way is the need for polymorphism, so exactly that you need partial different behavior within an otherwise same-behaving object. It's exactly one of the strengths of inheritance to allow specializations that stray away from the generic inherited parts. It's the whole point to allow easy modification for "non-standard behavior" instead of creating a whole replica that only differs on a miniscule level to its parent.

    • @techsuvara
      @techsuvara Месяц назад

      Being too stubborn with the "this should be standard everywhere" will absolutely be a terrible idea. You need to be flexible.

    • @defeqel6537
      @defeqel6537 Месяц назад

      Sounds like your understanding of OOP is missing an understanding of the Single Responsibility Principle and Open/Close Principle

    • @AloisMahdal
      @AloisMahdal 26 дней назад

      "track down 30 different places in code" -- lol, and you wish it's just in code, and not in DB schemas, logs, maintenance scripts running on some forgotten raspberry shoved in between drywalls, frontend (JS *and* CSS).
      say, adding a message severity level seems trivial but it can become a *real big adventure*[tm] real fast.
      or worse -- slow, with bonus side quests such as angry customers or dozens of people standing around awkwardly in warehouses being paid for doing nothing because what? you did *not* know that `danger` is a valid severity? you were naiive to think that it looks like enum, quacks like enum, but no oh boy this string is free like the bird and yes the whole frontend went down because, and only because you did not fall back on unknown string when f**ing switching style=`color: ...` in Vue?
      (i know, weirdly specific -- it did not happen in production to me but it drives me up the wall how easy it is to make exactly that mistake, when working on "hey do not over-engineer me" kind of codebase)

    • @AloisMahdal
      @AloisMahdal 26 дней назад

      @@Asto508 aside, but I recommend Christopher Okhravi's last video on this topic, "Only Use Inheritance If You Want Both of These".
      i love his channel, especially the last "wave" -- he came back after a long hiatus and now he's just killing it with much smaller and focused videos

  • @UNgineering
    @UNgineering Месяц назад +12

    "single responsibility principle" doesn't mean the class should only do one thing (although uncle Bob argues for that too), it means it should only be beholden to a single stakeholder, i.e. it should only have a single reason to change. but you're right in that this isn't the law, if it makes a small app way more complicated for the sake of adherence to SOLID, then it becomes counterproductive.
    Liskov is for Barbara Liskov :)

    • @Arcwise
      @Arcwise Месяц назад +6

      Finally someone who gets it. The biggest problem with SOLID is that it is being misunderstood at the most fundamental level.

    • @Sc0nes
      @Sc0nes Месяц назад +1

      Gather together the things that change for the same reasons. Separate those things that change for different reasons.

    • @halim7725
      @halim7725 Месяц назад

      Exactly. The architecture of your software should mirror the business units of your enterprise depending on it, or the diversity of your stakeholders in general.

    • @chickenmonkey88
      @chickenmonkey88 Месяц назад +3

      "beholden to a single stakeholder". Have you thought about what that means critically?

    • @shawnington
      @shawnington 11 дней назад

      @@chickenmonkey88 your mom

  • @muslim8622
    @muslim8622 Месяц назад +19

    Hi Prime, generally I like your take on programming, but on this one, I kind of disagree with some of your points.
    The Single Responsibility Principle might seem like overkill for a small project and I agree with your stance, but as the project grows and its components become critical and sensitive, separating concerns becomes more valuable. This helps to prevent one part from crashing the entire system. For example, if the rendering layer crashes because of issues with the cache, it doesn't make any sense.
    The Liskov Substitution Principle isn't about advocating for inheritance per se, but rather highlighting some of its challenges. Classes should be inherited based on their behavior rather than just their properties. If class A inherits from class B, we should be able to use B wherever A is used. In this regard, using composition instead of inheritance makes sense. Using an interface or trait enables us to define a contract for class behaviors, so if A and B used interface/trait, they can be exchange
    As for the Open-Closed Principle, I don’t think there can be a valid argument against it. As you are saying everyone uses the strategy pattern, everyone uses the Open-Closed Principle. "Closed" refers to consuming an API, while "open" pertains to building this API. When we say "closed," we imply that the main logic of our application must be “stable” and our main free from considering implementation details. For example, when I use a library, an API or even a (high-level) language, I focus on the algorithm; I write it, my logic is correct, so my code is completed. If there's a bug or a need to optimize the tool, change the database, or tweak something under the hood, it's not my concern-it's implementation details. And if there’s a new feature landing on the API, it’s good. But the main logic of my application shouldn't change because of such changes.
    My general stance in programming is just don't over-abstract, don't jump the shark. There're good patterns and good practice but not always necessary.

    • @LyleChristine
      @LyleChristine 18 дней назад

      Very well articulated and I agree on your critique.

  • @rupertsmith6097
    @rupertsmith6097 Месяц назад +9

    You can do SOLID in functional programming too. Interface = higher order function.

  • @ProfMonkeys
    @ProfMonkeys Месяц назад +1

    The comment about knowing when to abstract and when not to abstract really resonated with me.
    My sense is that the SOLID principles become more valuable as your app gets larger and more complex. When you are first writing a small app, many of the principles cause more problems than they solve. When it is easy to execute on them, great, do it. When it is awkward or difficult to do it, wait until you actually have a need to actually do them.
    Many times, the best choice is to just be mindful about how you can avoid closing doors on your design unintentionally.

  • @ThundersLeague
    @ThundersLeague Месяц назад +2

    I really love those SOLID arguments of "Well if I add a Printer interface now, _when_ I will need to add a Database Printer, it will be just implementing this interface".
    And then:
    - 99% of the time this reality never comes because business priorities change, or your project gets cancelled because you spent way too much time trying to come up with the proper abstraction instead of implementing business value
    - 0.9% of the time it does come but you still need to refactor it anyway because printing to a log file vs printing to a database is different enough that you couldn't anticipate everything
    - 0.1% of the time you actually created the right abstraction, but even then you spent 2+2 hours upfront on the Printer interface + FilePrinter, and another 1 hour later on the DatabasePrinter, vs 2 hours upfront on only the FilePrinter, and 2+1 hour later on the Printer Interface + DatabasePrinter.

  • @ionuttiplea4666
    @ionuttiplea4666 Месяц назад

    After watching your videos for a while, on topics like this and more, now I can have arguments with my Senior Colleagues about stuff related to this, and man that is a lot of fun.
    Related to this video's topic, I actually happened to stumble into a bad abstraction, and because of your videos I was able to identify that it was bad
    Keep up the good work @ThePrimeTime

  • @zbyniew
    @zbyniew Месяц назад +1

    Your enthusiasm is infectious. Love the content, man

  • @kashperanto
    @kashperanto Месяц назад +2

    It's funny, because the original meaning of "single responsibility" was that the code is responsible to/focused on the needs of one person/stakeholder/user, and has *nothing* to do with how the code is literally structured. From Wikipedia (but I heard this elsewhere, too):
    "The single-responsibility principle (SRP) is a computer programming principle that states that "A module should be responsible to one, and only one, actor." The term actor refers to a group (consisting of one or more stakeholders or users) that requires a change in the module."

    • @isodoubIet
      @isodoubIet 29 дней назад +2

      That's a remarkably useless principle.

  • @CaptainWumbo
    @CaptainWumbo Месяц назад +2

    I think the sad thing is a lot of the time that "amateur" 300 line function we wrote when we started is where we come back to when we realise having 1000 lego blocks all over the floor is not nicer than having one lego millenium falcon. Your lego blocks are the primatives of the programming language itself, the for loops and the structs and whatever. You don't need to make your own weird duplo blocks.

  • @joejoesoft
    @joejoesoft Месяц назад +2

    This is the classic YouArentGoingToNeedIt argument of Extreme Programing. I used to spout the "gospel" of XP early on until I understood it's major flaw - the refactoring nightmare. A collection of DoTheSimplestThing turns into an unreadable and unmanageable mess so slowly, you often don't see it until the fixes are enormous. The hidden part of "DTST" is that you have to refactor as you go, and knowing when to do this isn't trivial. This is the hard part.
    SOLID it kind of the opposite. The SOLID approach requires a fantastic base to even work. There are few people that can design a non-trivial API/Library/OO base that's flexible, easy to extend, and doesn't need to be changed. This is the major flaw of the paradigm; bad choices early on tend to become permanent.

  • @canepaper967
    @canepaper967 26 дней назад +1

    For every rule in programming there are exceptions. I prefer to look at writing code as just a selection of rules of thumb, where there are preferred ways to do it and non-preferred ways that are ranked in order of most to least appropriate. But sometimes you have to select the thing at the bottom of the list because that's the only way you can reasonably implement it. Sometimes a function HAS to do more than 1 thing for efficiency reasons because otherwise you'd be looping through the same data multiple times. Sometimes a function HAS to return more than 1 value for that same reason. As long as you understand the code this is perfectly valid.

  • @pseudocoder78
    @pseudocoder78 Месяц назад +7

    I think ThePrimagen should push his new paradigm: SOLIDR, single responsibility, etc, etc, etc and then R for "If all else fails just RAW DAWG IT IN THERE WHEREVER YOU WANT"

  • @scoutchorton
    @scoutchorton 27 дней назад +1

    17:17 “you abstract when you realize the abstraction”
    Love this principle. I’m building some Rust code to translate data between two programs, including a common format I made to make processing easier. During that process and discovery of how the two applications have their data formatted, I’ve discovered ways I can abstract and make common functionality between the two. I didn’t start with that, but discovered it as I realized two things worked similarly.
    Also the abstractions under Single Responsibility are totally not needed and the exact reason why I hate Java. I shouldn’t need 5 classes to read a text file

  • @tylerbreau4544
    @tylerbreau4544 Месяц назад +2

    About SOLID programming being good but preferring to be loose and not a requirement. I think this also applies to programming paradigms in general.
    Yea, you can program every single thing strictly following OOP.
    Or you can realize, this little thing is only in used by this 1 class and can be a single function.
    This little task doesn't even need its own function.
    Paradigms and these approaches to organizing code or guidelines, they're all tools. A good programming knows which tool to use for a particular task.
    As a general rule of thumb, yea single responsibility is a good idea.
    On the job, you're balancing readability, time to write & design, effectiveness, maintainability, performance and whatever other things I missed.

  • @n8ged8
    @n8ged8 22 дня назад +1

    Totally agree to all the statements in this video! Before you start programming you have to think about what you want to deliver. Do you want to build code that is very flexible and general because it will be part of a project that changes the next 10 years or do you want to deliver specific code for a specific problem that will not change. I almost always choose the last solution which means less code, less (abstract) thinking and less effort regarding time spent which means being very efficient and very cost effective. And this year my new friend ChatGPT solved some annoying or complex coding problems for me (but you have to know how to use this tool) so this makes it even more efficient.

  • @ErinCollective
    @ErinCollective Месяц назад

    the only time i ever use inheritance is encapsulating logging into errors and creating a typed instance of a generic typed interface, like something becomes somethingInt : something

  • @firetruck988
    @firetruck988 Месяц назад +2

    Here's my design principles: Make it as simple as possible without compromising function. All other design principles are sub to this, and are used when they support simplicity in a given context.

    • @defeqel6537
      @defeqel6537 Месяц назад

      If you have 2 pieces of related code with very similar functionality, which is the simplest: to use shared data+code for the similarities, or to implement them separately?

  • @jaymaj21
    @jaymaj21 28 дней назад +2

    I feel that the real problem is the appropriation of existing connotation-laden english words like clean, or agile, or solid. If he called it Robertist Code instead of Clean Code, or Martinist Manifesto instead of Agile Manifesto, or UncleBobist Principle for solid principle, I would have nothing against it. Just imagine how creepy it would be if religions were named using existing everyday words - say if we had to use the word "genuine" for "Christian" or "honest" for Hindu and so on.

  • @flannn6
    @flannn6 Месяц назад

    It is really cool how you understand the ideas behind each language. As a go developer i feel well represented by you =)

  • @wtfusernamecrap
    @wtfusernamecrap 9 дней назад

    Thanks, fully agree. It's pretty wild to advocate for abstractions in a world where many coders can't even name a function properly, let alone prevent the name from being a straight lie after the third implementation change. Write a straight forward solution. Write it a few times. Get the functionality right. Get the performance right. Profile. Discover where abstractions can be of use and implement with care. Profile again. You know how to profile your project, right? Make conscious decisions based on the reality of your project about which actions and abstractions benefit your. specific. project. That's the only thing that matters. Also, a project driven by a single person will drastically differ from a codebase maintained by a thousand people. It most certainly did not enter this world looking like the latter, for very good and important reasons.

  • @blubblurb
    @blubblurb 22 дня назад

    When I went to make my CS degree we had a Software Architecture course and of course SOLID was teached. After that I tried to do it right, apply it wherever I can use clean architecture etc. I was so hard trying to do it right that I couldn't get done anything anymore. After a while I stopped caring and just tried to solve the problem with the simplest solution I could bring up. Productivity is way higher, the code is IMHO better too. At least if I work with other people they are able to make changes in my code that's a sign to me that the code is good enough.

  • @saheemsiddiqi3220
    @saheemsiddiqi3220 Месяц назад +2

    I feel SOLID applies well at the overall class design level, but it starts to become a problem if you need to defer to these principals, line by line or property by property of a class. Take OCP. The first thing you need a clear understanding of is if, by design, you want the class to be extensible. If the answer is no, then relax the need for abstractions. SOLID needs to be applied in the context of an architecture, where you really want to focus on these principals where "parts" of your architecture interconnect, but within...you can afford to be a bit fast and furious with how you code things. And if you're wrong, then refactor.

  • @YaserFarid
    @YaserFarid 29 дней назад +1

    Totally agree with you, I like my program with very minimal classes, otherwise, it gets confusing really fast.
    I can have classes (as I make the embedded programs most of the time)
    LCD_Display class, Communication, GPIO, sensors, that is it, I would not add extra things to make it more confusing.

  • @RobRoss
    @RobRoss 26 дней назад

    Designing classes to be inherited from is *hard*. It takes effort and skill. Java has new features that help with this. They have default methods for interfaces. So you can add new API (methods) to an interface, and specify a default behavior (implementation), so you don’t break any classes using the modified interfaces. And now Java has sealed classes, which let you control what classes can inherit from a class. You can lock down your inheritance model to prevent problems down the road with people using your class and being brittle to changes in the inheritance hierarchy. And you can allow one specific class in your hierarchy to be inherited from and you design that one class for inheritance. Josh Block has a chapter on designing classes for inheritance in “Effective Java.”

  • @webdavis
    @webdavis 3 дня назад

    I think a lot of people forget that SOLID facilitates the hell out of testing. It absolutely does introduce code hunting into your code base, from the production side. But go look at the tests. It’s kind of nice that they actually split behavior. I’ll take that trade off any day. Trying to understand single tests that map onto multiple behaviors is a nightmare. Are there tradeoffs? Yes, but for the most part SOLID is a positive

  • @bigpest
    @bigpest Месяц назад +44

    Abstractions like the ones SOLID calls for make you *feel* like you’re making things easier and more maintainable.
    Really, you’re just hiding the actual function of your code underneath a coat of pretty paint.

    • @playthatsoloboi3705
      @playthatsoloboi3705 Месяц назад +4

      hmm, that depends actually

    • @LusidDreaming
      @LusidDreaming Месяц назад +9

      I feel like this is very true for single responsibility. One thing that is rarely noted is that separation of concerns is mutually exclusive with locality of behavior. So you look at a small class that does only one thing and think "oh, this is simple to understand." But then you start actually working with the codebase and realize that a single process will span several files and contain several layers of indirection that make it very hard to track the actual control flow (made even worse if you add inheritance).
      Conversely, procedural code with high locality of behavior often looks ugly and disorganized, but when working with it you realize its very easy to read and understand all the code paths.
      There are tradeoffs with both, but I personally consider locality of behavior to be the most important, and only move away from that of testability becomes an issue.

    • @Asto508
      @Asto508 Месяц назад +6

      That's only true if you're doing it wrong. There is overengineering and pointless abstraction, but there is also the opposite that is equally bad. If an abstraction is not serving a real (foreseeable) purpose, then it's just a useless layer and simply a wrong application of what abstraction is supposed to solve.
      The hammer isn't bad because you try to drive screws with it tbh.

    • @broadestsmiler
      @broadestsmiler Месяц назад +2

      You robbed my face. Please give it back. :(

    • @mirkogeffken2290
      @mirkogeffken2290 Месяц назад +1

      Yes that is the very foundation of the principles. You want to hide complexity and avoid change until you need to dive deeper. There is a difference between algorithmic and domain complexity. You want to make it easy to understand code and avoid change to hard things while keeping comprehension at a high level. This is a hard problem to solve, but optimizing on readability is mostly correct until you need to optimize. His video is spot on to evolve towards this balance. You are 99.99% going to get this wrong up-front. Don’t whiteboard it, build it, then optimize as needed. None of the fundamentals of SOLID are wrong, just know when to apply and a need to know when to violate them. They aren’t prescriptions, they are guiding principles.

  • @wforbes87
    @wforbes87 26 дней назад +1

    the last year I've been dealing with a project of like 16 microservices in typescript, all using really dense solid patterns... every opportunity to use an interface or a factory was taken, even if there's just a couple entities involved. Want to add a new database table? get ready to write 7 code files to follow the pattern. want to find a bug? get ready to drill down through layer after layer of abstraction. I mean, the code can be scaled out into infinity and beyond, with multiple teams working on it happily... but its a 300 user internal app with a pretty limited scope, and like one or two developers. I've really come to absolutely worship simplicity.

  • @james.lambert
    @james.lambert Месяц назад

    My first rule of programming is write reusable code. Reusable code makes it easy to not repeat yourself, it is easy to "reuse" in tests to verify it, it is easy to reuse existing code when refactoring into new abstractions. After finishing a function or class if you have a hard time imagining that thing being used outside of the specific use case it was written for then you should rethink the design.

  • @Dan_Diaconescu
    @Dan_Diaconescu Месяц назад +31

    your voice is smoother since quitting Netflix, faang bad for health confirmed

  • @cod3r1337
    @cod3r1337 29 дней назад

    I think SOLID is a useful starting point to teach beginners to start thinking more carefully about the structure of their code. Just be absolutely clear that those "principles" are not to be treated as dogma but rather as little nudges to get the thinking process started.

  • @MrSpyTubes
    @MrSpyTubes Месяц назад +2

    SRP means that the module should have one reason to change. Meaning that if the marketing comes with a request for change and the finance comes with another one no single module should need to change for those two requests, but rather two modules

    • @Daniel_Zhu_a6f
      @Daniel_Zhu_a6f Месяц назад

      there is a better idea: to organize modules as you need for testing, hot reloading and efficient development in general, without trying to solve unsolvable questions like "what is a single responsibility?". if the program is simple and small (5-10K loc), you can just dump everything in a single module.

    • @defeqel6537
      @defeqel6537 Месяц назад

      *at least two

    • @isodoubIet
      @isodoubIet 29 дней назад +1

      "Meaning that if the marketing comes with a request for change and the finance comes with another one no single module should need to change for those two requests, but rather two modules"
      That's completely unworkable.

    • @blubblurb
      @blubblurb 22 дня назад

      @@isodoubIet Exactly. It sounds nice in theory but never happens in practice. And What @Daniel_Zhu_a6f says is also true, define "single responsibility". Sounds easy but it's not. You will abstract way too much to reach that unreachable goal.

  • @dripcode2600
    @dripcode2600 Месяц назад

    code to interfaces -> SOLID -> Software Design Pattern (Why do this? Assists in good automated testing. Why automate test? Automated Test are like the business logic compiler.) Why are good test important? To avoid South West Airline software crash embarrassments and costs.

  • @TurtleKwitty
    @TurtleKwitty Месяц назад

    Inheritence is really nice for UI/renderable framework, being able to define a simple say 2D renderable rectangle and then let subclasses figure out the rest of the details themselves without the framework doing more work makes implmenting it a LOT easier -- BUT -- inheritence/OO definitely gets shoehorned into a lot of stuff that doesnt belong for sure

  • @efkastner
    @efkastner Месяц назад +7

    30:40 this!! it’s the spidey sense that you might want to extend later, but enough experience to hold off. you don’t make it impossible to extend, but you also don’t try to make it extendable in every possible dimension

  • @aybgim3850
    @aybgim3850 18 дней назад

    Liskov only tells how NOT to use inheritance, it doesn't tell you how much to use it. If anything, it prevents you from abusing it in a really bad way. And it applies to implementing interfaces/traits as well, so totally applicable to Go and Rust

  • @mattetis
    @mattetis Месяц назад

    Misunderstanding Liskov.
    **Implementing an interface is subtyping.** Passing an object which implements an interface to a function which expects said interface, and expecting the program to work, is the Liskov substitution principle.
    It just happens to be that inheritance is also subtyping of the base class. Using abstract classes as the interface is the problem.

  • @Archheret1c
    @Archheret1c Месяц назад

    In general principles and "recipes" are good when learning the ropes. It's a bit like making food, if you have very little experience with cooking and want to make a more advanced dish following a recipe to the letter is probably a good thing. When get more experience you can start to deviate, and after a while you start to understand what parts of a recipe you can deviate from and which part is crucial to reach your goal.

  • @TheTubeYou251
    @TheTubeYou251 16 дней назад

    Liskov doesn‘t say that you want to have a lot of subclasses. It just says that if you write a subclass, you better don‘t do unexpected things there that breaks code which codes against the base class. If you never write any subclass, you trivially follow the principle.

  • @JacobSantosDev
    @JacobSantosDev Месяц назад +1

    1. One should not start their design with SRP. Start with DIP or ISP. Or better with an implementation and start seeing where patterns are established and contracts can be formed.
    2. All subtypes inherit, including when implementing interfaces. It is just that you can only satisfy LSP when working with interfaces. Just like OCP does not necessarily imply inheritence.
    3. Bro needs to find DDD.

  • @DanielMPries
    @DanielMPries Месяц назад

    I teach it like this. Writing software is a lot like music, we learn foundations and guiding principles, structure and theory. Once you developed mastery in those areas, go ahead and break the rules. That's where the magic happens. But I'll be easier to ease back into the foundations if things go sideways than it will be to establish foundations when you started out without the rules in mind. Much like art, negative space is also important. It's ok to leave things out. You don't have to be comprehensive if code is going to be unused.
    Worse yet though is someone who refuses to know the rules. They can be dangerous contributors and toxic collaborators

  • @ITPMMentor
    @ITPMMentor Месяц назад +1

    Appreciate the solid reflections on this topic. Hard to get inputs from practitioners sharing examples of how to effectively apply these principles in the real world

  • @spikedgav
    @spikedgav 15 дней назад

    We could totally mess up the authors day (and get them to think about what they're doing) by telling them that on tuesdays, wednesdays and thursdays, customers get a free bookmark with any purchase of 2 or more books.
    NOW they have to actually think in terms of abstraction and might have to write a new article about business logic

  • @vanstanian
    @vanstanian Месяц назад +1

    A comment on Liskov principle: is just about behaviour responsiveness. I mean, if you have an interface and multiple implementations on it, you have to expect a consistent behaviour. Sometimes in Java (a fundamentaly broken languaje bc of being older), you have some trouble dealing with some types as Lists. I found that when you use unmutable lists, there are functions that exists in the interface like f.e. .add() which throws an exception, violating Liskov. That becomes a pain in the ass in the moment you realize that the behaviour is different from the ArrayList. Liskov talks about what you should expect with assurance about an abstract type, just to do the life of other developers easier as possible.

  • @MikkoRantalainen
    @MikkoRantalainen Месяц назад +1

    I totally agree that you shouldn't abstract before actually *feeling* the need for it. Say you abstract your invoice printing from the start to support printing to JSON and stdout. However, when the boss then says you have to support HTML, too, and by the way, HTML needs company header, you have to redo *every abstraction* you already did for the printing stuff so support injecting the extra information to pass the header without using hardcoded header or global configuration values. And the same thing for every other extra dependency what your future data print needs might be.
    I know I've done lots of unnecessary abstractions during my career and it's not an accident such thing is called astronaut architechture: you try to guess what kind of interfaces you might need if this specific feature would be used by NASA during the Mars mission. And since you cannot possibly guess that correctly, you'll always miss something and have something extra that even the Mars mission is not going to need.

  • @zootsuitpenguin
    @zootsuitpenguin Месяц назад +1

    Ive always felt bad that i typically ‘script’ much in the way that i see you’ve done here taking the tradeoff for ease of writing vs strictly adhering to these principles, glad to see I’m not the only one! #gatekeepingprogramming

  • @MarkWilds87
    @MarkWilds87 28 дней назад

    Developers usually tackle their problem with technical first, functional later.
    Instead it should usually be functional first, technical later.
    But getting to understand the functional part of your program is really dependent on soft skills, communicating, etc... ughhhhh
    Basically what Prime says at the end: "Refuse to abstract what you think you need, instead build the thing you know you need".
    ~Prime: "You abstract when you discover the need of the abstraction."

  • @fabyao
    @fabyao 23 дня назад

    I personally find the SOLID principles useful. When one works in a large team with a large code base, without any design and code quality agreements, the code-base can quickly turn into spaghetti code and mixed concerns. When done properly, SOLID allows you to replace any bottleneck or compromised part of the system with minimum effort. As a concrete example, there was a vulnerability found in a third-party logger library. I believe it was a Java library. With SOLID, replacing the affected library is done in one place with ease. Unfortunately, most developers use logger libraries directly without programming to an interface. This means finding all the instances in your code-base to replace it. As a Software Engineer, I strive to implement code that welcomes changes without going down the rabbit hole of a major code refactor. SOLID is one way to achieve this. Unless your are experimenting and not seriously thinking of a "production ready" code, I would argue that a design principle whatever it might be must be carefully considered. The idea that one can write "dirty code" and refactor later to "clean code" is, in my humble opinion doing the job twice. A good analogy is trying to build a house by having a "quick and dirty" foundation which will be revisited after the house is built. If the house is still standing

  • @janwilmans5954
    @janwilmans5954 29 дней назад

    LSP is not just about inheritance, it can also just be the function interface of a strategy pattern; maybe some languages do not handle that very will, but for example in C++ it works really nice.

  • @assombranceanderson6175
    @assombranceanderson6175 Месяц назад +2

    Currently working on a medium project which would probably be very annoying without inheritance.
    I'm handling instructions of several types, and operations on these instructions. Avoiding standard inheritance would just lead to emulating inertance by having a type hierarchy hardcoded for each subtype of instructing...

    • @Asto508
      @Asto508 Месяц назад +1

      I honestly think the "conflict" comes from people only looking at it from a pure coding point of view vs a maintenance point of view. The more you have to maintain existing code bases, the more (well thought) inheritance pays off and you are glad that you only need to touch this tiny part over here instead of re-writing 50% of the code base over there because your predecessor thought abstraction is just a waste of time.

  • @heavenaldrico
    @heavenaldrico Месяц назад +2

    3 seconds in, true.. my only problem with the SOLID principles is that I forgot what they stands for few hours after reading about it

  • @SimGunther
    @SimGunther Месяц назад

    Code is data, data is code
    Feet used to the pipeline dataflow workshop factory model to translate data into different kinds of data, but no side effects unless it's at the beginning or end of the function.

  • @nuKsBe
    @nuKsBe 15 дней назад

    The single responsibility can (and actually should in many case) be to handle the dataflow. This both follows locality of behaviour and SOLID. The point is not to have as many abstraction or unit of code as possible. The point is the opposite, to have one single place per responsibility rather than multiple places per responsibility. Then you have control over the granularity of the responsibility, at long as it is one per unit. It can be a "wide" responsibility or a small one, but it must be properly defined, unambiguous and unique/single.
    Done right, Single Responsibility Principle and Locality of Behaviour are the same thing.
    Liskov tells when you can use inheritance, not that you should do it. Composition over inheritance.
    Inheritance is good for modularity and encapsulation. If others developer want to extend or modify a feature and the original developer want to control to which extent they can do it.
    Lots of great use cases (UI, plugins, ...)
    A good way to look at SOLID overall is that you can apply it in non-OOP as well. The paradigm itself doesn't matter as much as the principles themselves. For instance using higher order functions etc.
    I may understand it feels counterproductive nowadays. I think it is because most of the time it is being understood from a place where things are already being built upon standardized ways / abstractions / frameworks that are actually already following SOLID principles. For instance MVC and all related. Thus there is no need to add SOLID, because it's already being done.
    A good counter-case for OOP is when you need both targeted and global customization through code (declarative customization alone not being enough for the use case etc.). For instance, in multi-tenancy setup where you may have to be able to slightly change business rules at any point of code depending on the clients. Considering you'll then have to maintain the different codebases in parallel over long period of time as well.
    This can be done totally fine without OOP, but in any case you'll need a very good design for this. And OOP aims to provide the tooling for that. If you don't have the experience to use it well, and I do think it does take a lot of trial and error before getting OOP the right way, it may induce too much complexity. But the same goes for anything that allows high level of abstraction, ie. higher order functions etc.
    I do believe you pinpoint a lot of major caveats however and your video has a lot of value. From my perspective the title could very well be renamed "how to do SOLID the right way", rather than SOLID - No Thanks", and it would still work out fine overall.

  • @strider5245
    @strider5245 Месяц назад +1

    I love Prime, I used to call it: this is over engineering.
    Or why are you giving me a spoon larger than my soup bowl?!

  • @TysonLondon
    @TysonLondon Месяц назад +1

    Amen, "you abstract when you see the abstraction" 🙏

  • @tomasnovellino5980
    @tomasnovellino5980 Месяц назад

    One quick disclaimer I've learnt in practice. Solid is heavily focused on OOP and, more particularly, for Java. Even Python, C++ or .Net have ammendments.

  • @Altirix_
    @Altirix_ Месяц назад +11

    ive seen people take Single responsibility too far in our codebases. for example. there was this part of the project that would transform a grid of points. ie rotation and translate.
    these are all simple concepts that can be done in a few lines. whoever originally wrote it did every math operation in its own function. every function was a single line.
    the problem with the idea of single responsibility is the line is ambiguous. because, technically every function had a single responsibility of a single math operation, and doing this made the code much less readable, who could have guessed. end of the day the programmer still has to understand where to draw that line, where abstraction is a benefit and where it is not.
    in some ways these principles we have do end up feeling like they say a lot of nothing as there is no one size fits all and you end up losing the nuance to the when and why

    • @ncpeaksean4278
      @ncpeaksean4278 Месяц назад +5

      Thats not the SRP concept, youre wrong, it's not about to split every task in functions, it's about every function only would have a reason to change, no many reasons.

    • @TurtleKwitty
      @TurtleKwitty Месяц назад +3

      Seems totally fine for your grid math lib to be split in multiple functions though?
      Unless you meant in the sense that you had a specific set of operations to perform in for the final output nad they split that pipeline up for no reason? IF that's the case then yeah that's wack as hell, that's why I tend to describe my functions in terms of "data op" "pipeline" and "transform" the math operations (rotate, translate, etc) would be a transform and seperate but the pipeline for the final output would be in its own thing (func calc(in){in.rotate(1, 2).translate(3, 4).rotate(5,6)}) and the data op functions would befor getting the in object from a data source and another for sending that off somewhere

    • @emmanuelbustos2532
      @emmanuelbustos2532 Месяц назад +4

      SRP is not separation of concerns. They are different things but are confused all the time. Everyone thinks they know what SRP is but no one really does. It's an organizational principle, not directly a code related principle.
      The way I understand it the best is that you don't want multiple stakeholders requesting things in a single software entity (i.e. but not necessarily a class), as they are likely going to request things that conflict with each other.

    • @Altirix_
      @Altirix_ Месяц назад +1

      @@TurtleKwitty yeah, it was basically taking one 2d coordinate space and mapping it to another. just a linear transform and a rotate
      i was tasked to convert it from only being used to converted between two hardcoded coordinate spaces to be user controlled at runtime.
      it did the latter, there were like 16 functions responsible to convert between the two. so it was like func convertToLocationX(x){return x + LOCATION_X_ORIGIN} and below it func convertToLocationY(y){return y + LOCATION_Y_ORIGIN} rinse and repeat

  • @hoppy6437
    @hoppy6437 Месяц назад +1

    Like most principles in software development it shouldn't be dogma. Scrum was particularly annoying because it wasn't working well for most teams but the problem was never scrum, it's that you weren't practicing it religiously enough.
    Understand what goal a principle is trying to accomplish and either do it, or don't with the understanding of what you're trading for it.

  • @bdinh3130
    @bdinh3130 Месяц назад +7

    Having been forced to follow solid I can say the "single principle" just leads to a lot of middle man classes that's sole purpose is just to be the bridge to how to classes interact. Sometimes you can get away with just using a event/messaging system but when the coupling is tight then the easiest and simplest solution is just passing in a pointer. At least in C++. But when you are forced to follow solid that is frowned upon and you have to write classes with long ass weird names that only confuses everyone after a month of not touching it.

    • @avwie132
      @avwie132 Месяц назад +1

      Then the people who forced solid on you didn’t understand the S.

    • @chickenmonkey88
      @chickenmonkey88 Месяц назад

      @@avwie132 Neither did Bob Martin

  • @juanjosefarina
    @juanjosefarina 26 дней назад

    20:00 there is still a bigger problem with touching existing code to add new functionalities, and that is that if a bug arises, it's gonna be harder to troubleshoot where the bug is, it could happen in the code you wrote, or the code that has already been communicating with that older portion of code. If you add the new functionality outside the older code, you can be pretty sure the problem is in the new functionality alone.

  • @tomasnovellino5980
    @tomasnovellino5980 Месяц назад

    I'm would extend your conclusion.. over designing and adding extra layers of abstraction is surely planning for "what the problem should be" but, my grains of salt here are. More often than we imagine we are also designing fie "what the problem WILL be" or even worst "what the problem COULD be".
    We should just go for what the problem IS. Remember, S for single responsibility.. present, not future, desired or possible

  • @DanielS-cu2ic
    @DanielS-cu2ic Месяц назад +1

    Do your best to follow the rules until you have a good reason to break them.

  • @isodoubIet
    @isodoubIet Месяц назад +1

    "I'd argue that you should keep better indentation"
    Typical tabber code. "You can set the tab width to be whatever you want!" while ignoring that it looks like garbage on any setting except for his.

  • @joebowbeer
    @joebowbeer 24 дня назад

    The most important principle is to maintain a low cost of change. SOLID attempts to avoid design blunders that can increase the cost of change.

  • @ifeoraokechukwu1346
    @ifeoraokechukwu1346 Месяц назад

    Here's my 2 cents on the "S" in "SOLID":
    Single Responsibility Principle (SRP). I believe (based on my experience) that SRP does a very wonderful and perfect handshake with High Locality of Behavior (LoB). Now, most people like to define LoB as co-locating all the logic in one source file. But, it can also be co-locating logic spread across many source files in one folder (especially for non-trivial software logic). SRP ties perfectly into this by grouping (or co-locating) things that change for the same reason together and separating things that change for a different reason.
    When using SRP, people organise their codebase by. type instead of by feature - which is wrong. Organising your codebase by feature gives you higher LoB than than organising by type. So, you can only enjoy the true benefits of SRP by organising by feature (so you don't have to hop far around the codebase too much to understand the code). Making use of SRP in your codebase will always suck ass when you organise your codebase by type instead of by feature.
    Cheers ✌🏽

  • @robgrainger5314
    @robgrainger5314 Месяц назад +1

    FPOOP has been around at least the 1960s. OO originally rose from projects in LISP before there were even OO languages.

  • @etzabo
    @etzabo Месяц назад +1

    I program until whatever I want to do works with adequate optimization and readability so that I don’t have to determine whether or not the system I’m about to implement fits into some random acronym.

  • @OneAndOnlyMe
    @OneAndOnlyMe 11 дней назад

    Nah, definitely should have a separate InvoicePrinter class to handle printing 🙂
    In my real world enterprise, SOLID applies very much, especially open/close. Mainly because we are at a point where we can build solutions that leverage utility computing services, so decoupling code for quick and easy extension of a persistence manager is very useful, for example, we might use an AWS implementation of a service today, but next month we want to switch to the GCP implementation, in large enterprise where we're continually looking for cost efficiency, we would do this kind of thing quite often.
    When you're a long term employee of an organisation and you know how the organisation works, proactive abstraction can be easier and saves time.

  • @sdfsdf421df
    @sdfsdf421df 28 дней назад

    I like most this part: If you are in industry for sufficient amount of time, you will (eventually) grow into being able to see, why some rule is very bad long term (at least in some cases). Since you saw it fail multiple times. Then if you are asked about these rules, you can either lie, or somehow question them based on your experience. And that's the moment, when guys far more junior will bash you for "blatant ignorance" of basic principles. ~He does not even know that `X`!!

  • @worldwideweb7794
    @worldwideweb7794 Месяц назад +5

    Solid is great, but not when you are dogmatic about it. Always think about YAGNI and KISS whenever you want to abstract. Good devs know solid rules, the better devs also know when to break them.

  • @frydac
    @frydac Месяц назад

    SOLID doesn't say you HAVE to follow the dogma without critical thought and any violation is inherently bad design, uncle bob or any decent resource on the subject doesn't say this afaik.
    They are design 'principles', not design laws. I learned a lot by studying SOLID when I was a newbie, they give you a way to think, a way to spot what could be the underlying issue when you start repeating things you shouldn't and introducing dependencies in a way that you feel will make you regret later on, they are a tool to use in certain situations. Before I studied SOLID I felt things were going wrong, but didn't know why or how to deal with them, and solid gives at least some kind of answer (of course it's not complete) that may help in a given situation. They give you a names to use when discussing design with your team, like design patterns: "maybe we can use this or this here? Oh no, this or that requirement probably makes it a bad idea". or "When we do that we are violating this or that principle. Yeah, but it's ok because the chance of this becoming an issue later is very lo. Ah yes indeed." are possible/viable conversations one can have and are probably better informed and get better results when you know about the principles/patterns.
    'There are no zero cost abstractions' (by chandler carruth cppcon2019) is also a principle that should be weighed when thinking about introducing an abstraction, everything is a cost-benefit trade of. And of course you don't apply things everywhere, of course, noone sensible ever told anyone that this is the only correct way, which is usually the sense I get from ppl 'against' solid or clean code or design patterns, I think they are reacting to the ppl that interpret these principles as laws/dogma and try to force them on other ppl, and not against the principles themselves, as all of them (afaik) can be applied in a way that makes things better in the right circumstance, and there is significant value in learning about them.
    Anyway, I got baited again into commenting, so job accomplished I guess... 'sigh'

  • @LusidDreaming
    @LusidDreaming Месяц назад +3

    Imagine getting credit for just creating an acronym for someone elses work. Its like the Maclaurin series in math.

  • @ErinCollective
    @ErinCollective Месяц назад

    abstractions are more than just classes and interfaces though, like if you want to do ddd then you have additional abstractions (that you need because you already populate them) above the class / interface level eg project level, folders are better for namespacing but the ddd abstraction can be the top level directories too.

  • @andythedishwasher1117
    @andythedishwasher1117 Месяц назад

    Grug first write main function do thing need program do. If main function start look big or ugly, Grug find ugly part and put in different function. Grug name carefully, make sure Grug know where to find. Grug also name parameter carefully. If functions call each other, probably put in same file if file not too big. If file too big, put big or related functions in other file and import. Name carefully. This all abstraction Grug usually need.

  • @ryoriotwow
    @ryoriotwow Месяц назад

    Inheritance is very useful. Suppose you have a datastructure that combines multiple tables in a database because there is some validation across them. For instance a case-folder containing multiple documents, having a case-owner and some sort of access-validation scoped by user-session. Maybe you even have something stored in a completely different database, or off-site, like files for the documents in a one-drive or whatever. If you want to present this data in a web-application, you typically want to have API-endpoints returning DTOs of different degrees of implementation. I.e. one DTO that only exposes the most simple fast-accessed properties, and one or more inherited DTOs that expose further properties that the API might have to do a bit of work to retrieve the values for. That way, your front-end web-application can work with these different levels of inheritance and request the object that it minimally needs given the context, to display whatever it needs to display, and you don't have an API working overtime collecting data that is discarded.

    • @ryoriotwow
      @ryoriotwow Месяц назад

      Of course, you can always solve it differently. You could for instance separate out the properties that is load-heavy into their own structure, maybe it just takes in the case-folder id, and then you retrieve it completely seperately whenever you need it.

  • @steamer2k319
    @steamer2k319 Месяц назад +1

    Will watch later but here's my apriori take on SOLID:
    I think it has some interesting things to say, but it'd probably be easier to communicate / remind about the principles if someone were to translate them into English.
    EDIT:
    HAHA! First line out of Prime's mouth: "I forget what they are all the time"
    EDIT 2: Just noticed the article is different from the video title too 😄:
    ~"SOLID in plain English"

  • @juniorceccon
    @juniorceccon Месяц назад +6

    My preferred programming parading is FUNtional programming. Not to be mistaken with functional.

  • @mrpocock
    @mrpocock 28 дней назад

    So the squares and rectangles thing is a great example of how oop subclasses can be unintentionally evil. Consider rect.setWidth(20).setHeight(30) and ask what we expect getWidth and getHeight to return if we have a real rectangle vs an instance of square.

  • @kokizzu
    @kokizzu Месяц назад

    agree, start with concrete, use function pointer for dependencies so can be tested