7 ms·
> whereas complex functions fit on your screen typically Are the complex functions unit-testable? Do they depend on other units of work or other libraries? Doe
by TravHatesMe 7y ago
> whereas complex functions fit on your screen typically
Are the complex functions unit-testable? Do they depend on other units of work or other libraries? Does it have multiple responsibilities? You are probably following most of what SOLID entails.
I find it funny that HN consistently bashes SOLID, I feel like SOLID has been misrepresented. They are _guidelines_ for development, they do not dictate everything. They might influence or support a decision.
Those who bash SOLID: have you worked on gigantic projects that are in active development for decades? I advocate for SOLID is because I have witnessed first-hand its great benefits. I have built and worked on plenty of projects that apply these principles, I have seen wonderful open-source projects that embrace them. And of course I have seen adverse effects from it (eg. your linked article complains of innumerable, non-sensical interfaces) but that is mostly due to inexperienced developers that don't get it. And of course there are some devs/architects that go overboard, introducing premature abstractions, etc.. To those people I say YAGNI. The point being: following SOLID doesn't guarantee nice design. It is easy to produce shit code following SOLID, but it is even easier without it.
At the end of the day, there are trade-offs. I think using SOLID as development guidelines produces a scalable codebase divided cohesively into units of work.
- stcredzero 7y agoI find it funny that HN consistently bashes SOLID, I feel like SOLID has been misrepresented. They are _guidelines_ for development, they do not dictate everything. They might influence or support a decision. And of course I have seen adverse effects from it...but that is mostly due to inexperienced developers that don't get it. Whether SOLID is seen to pay off in the medium term, or the long term, or the very long term, is dependent on environment. In some environments, the payoff is apparent sooner. In others, it's only longer term. This is why inexperienced developers may not get it. Of course, that beings up the question, "How can we better communicate the benefits?" Can we document and present those, from the actual history of the project? EDIT: To better relate this to my other comment in this thread, a problem with SOLID in environments where there's a lot of bookkeeping for the compiler's sake and compile times slow down the edit-test cycle, less experienced developers are going to first notice, "Hey, this stuff makes me flip back and forth between files!" If they never see the benefits, they're naturally going to conclude it's a bad thing.
- jbverschoor 7y agoGuidelines sure, but programmers are extremists.
- vkou 7y ago> Those who bash SOLID: have you worked on gigantic projects that are in active development for decades? Yes, and by far the worst part of them is the dependency hell problem of a sufficiently mature front-end. It gets sand in your cornflakes during development, testing, and debugging. Imagine you're writing a front-end in this mature codebase. What injection bindings do you need to instantiate a FooUIWidget, which contains a BarUIWidget and BazUIWidget, and a few new data types, relevant to the business logic of FooFeature? Who the fuck knows! You have a rabbit's nest of nested dependencies, you have no idea what part of the system owns which data change, or what cascading effects that data change has. Oh, and when you decide to move FooUIWidget out of ParentUIWidget into UncleUIWidget, good luck figuring out which dependencies it needs, which need to be removed from Parent, which need to be added to Uncle, which need to have alternative bindings added (Because Uncle already provides them, but they are not what Foo needs - your code compiles, and gets no run-time Dependency Injection errors, but your values are silently bound wrong behind the scenes.[1]) Unless, of course, you do something sensible, and instead of having each bit of your system depend on 20 things provided by dependency injection, just build the bloody thing right the first time, by using event listeners and MVVM. [1] Oh, and of course, neither your compiler, nor your DI framework is mathematically capable of telling you that half of the dependencies you're providing for Parent are no longer used for anything. Go get your coal miner's hard-hat, finish up your will, sign the waiver about black lung, and go delving through your dependencies.
- TravHatesMe 7y agoFront-end (web?) is a more narrow domain than what I was speaking to. It sounds like you are pointing out specific issues that you encountered when working on a particular project. How is SOLID responsible for these issues? The acronym represents guidelines. What you are describing sounds awful. IoC can get nasty when developers are inexperienced and off-the-leash. Keep in mind that everyone is ignorant. We all have different experiences with different technologies on different codebases.
- vkou 7y ago> Front-end (web?) is a more narrow domain than what I was speaking to. It's the domain where proper architecture matters the most, because it's hard to get it right. > It sounds like you are pointing out specific issues that you encountered when working on a particular project. If by 'particular project', you mean every single FE project that I've worked on, that made unopinionated use of dependency injection, sure. > How is SOLID responsible for these issues? 'LI' doesn't do any value add for these problems (You don't use all that much inheritance, or define very many interfaces when working on front-ends), and 'D' is actively harmful, because it paves the road to dependency injection. I find posting events to a bus to be a lot easier to deal with, then dealing with a spaghetti of objects interacting with injected dependencies.
- crimsonalucard 7y ago>I think using SOLID as development guidelines produces a scalable codebase divided cohesively into units of work. The problem with Object oriented programming is not any of these things. The problem is that an object is a bad choice for a unit of work. A good analogy is bricks and construction. If a brick represents a unit of work to construct a wall, object oriented programming represents a brick with jagged faces. This is why, no matter how deeply you follow these guidelines you will always have to build custom "interface bricks" (aka glue code) to compose jagged bricks together. GoLang solves the problem with objects by getting rid of objects all together, but the fundamental procedural function that it uses as a primitive of composition is also jagged in a way. GoLang procedures do not compose very well. There is a deeper primitive that programmers should model their code around that gets rid of the usage of misshapen bricks as the building block of programs. Bricks that compose with other bricks without glue. I leave it to you to find out what this primitive is, as you use it everyday to build misshapen objects. The original article talks about readability and simplicity. It does not talk about compose-ability and modularity. Both of the aforementioned traits have a strange relationship with readability and clarity. More modularity does not necessarily mean less readability in all cases but it certainly changes readability.
- james_s_tayler 7y agoI'm a big fan of SOLID too. It actually makes a great deal of sense. Re: your comment about "And of course I have seen adverse effects from it (eg. your linked article complains of innumerable, non-sensical interfaces) but that is mostly due to inexperienced developers that don't get it" While this is true, there is a deeper implication here. Assume programming talent is normally distributed. Now, ask yourself above what percentile do you have to be in that distribution to truly grasp the how/why of SOLID and to be able to wield it to solve problems. Now, ask yourself what percentile do you have to be below where you just go crazy with creating non-sensical interfaces and thousands of awfully named classes with single responsibilities such as "CustomerCommandMapEmbelisherConverter"? The real problem is that code bases tend to be horrible (also normally distributed!) because a lot of talent doesn't meet the bar and can't actually produce programs that aren't rubbish. In any org you'll find the quality of the code base is somewhere on a normal distribution. And you will find all the engineers somewhere on a normal distribution. You'll have a couple of brilliant people, a couple of horrible people, a lot of average people. The only time you truly see exceptional code bases that everyone stops and goes "wow, this is nice!" are the rare times the stars aligned.
- crimsonalucard 7y agoSOLID is contradictory and flawed when used with Object Oriented Programming. 1. Single-responsibility Principle. Objects should have only 1 responsibility Objects by default often have two responsibilities. Changing it's own state and Holding it's own State. 2. Open-closed Principle. Objects or entities should be open for extension, but closed for modification. The very concept of a setter or update method on an object is modification. Primitive methods promoted by OOP immediately violate this principle. 3. Liskov substitution principle. Subtypes can replace parent types. This principle represents a flaw in OOP typing. In mathematics all types should be replaceable by all other types in the same family, otherwise they are not in the same type family. The fact you have the ability to implement a non-replaceable subtype that the type checker identifies as correct means that OOP or the type checker isn't mathematically sound... or in other words the type system doesn't make logical sense. 4. Interface Segregation Principle. Instead of one big interface have functions depend on smaller interfaces. I agree with this principle. Though many composable types leads to high complexity. I don't think it's an absolute necessity. 5. Dependency Inversion principle. High level module must not depend on the low level module, but they should depend on abstractions. This is a horrible, horrible design principle. Avoid runtime dependency injection always. Modules should not depend on other modules or abstractions, instead they should just communicate with one another. If you are creating a module that manipulates strings. Do not create the module in a way such that it takes an Database interface as a parameter than proceeds to manipulate whatever the database object outputs. Instead create a string manipulation module that accepts strings as input and outputs strings as well. Have the IO module feed a string into the input of the string manipulation module. Function compositions over dependencies... Do not build dependency chains.