← Back to context

Comment by alcasa

15 hours ago

Maybe controversial, but I believe a lot of OOP/Clean Code patterns are the software equivalent of corporate BS.

Wildly controversial!

I look at OOP Patterns as standards and practices.

The same way we have building codes for staircases the framing of walls and electrical installations to prevent injury or collapse or fire.

Sure, you can dodge a lot of design pattern paradigms and still make a working application that makes money. You can also invent your own system when building your house and maybe nothing bad will happen. That tragedy hasn’t yet struck does not make the building codes bad just because you got away with it.

  • A decent chunk of OOP patterns was due to lack of language features, notably passing and returning functions

    • It's both.

      The *concept* of patterns makes sense. A shared language that developers can use when building things.

      The *reality* of patterns has been much less useful. The original ones were indeed a reaction to warts in the popular languages of their era. And as we tend to do in our industry, these have been cargo culted along the way and for some reason I still see people talking about them as first class citizens 30 years later.

      People don't seem to realize that patterns should be and are fluid, and as our industry evolves these patterns are evolving as well. A major difference between software engineering and the analogous fields people use when talking about patterns is those industries are much older and move less quickly

      1 reply →

    • Since a single-method object easily serves the role of such a function, that’s simply not true. Looking at the 23 GoF patterns, I can’t identify any that would be obviated by having first-class functions (or lambdas, as many OO languages nowadays have). Some of the patterns can employ first-class functions (e.g. an observer could be just a callback function reference), but the pattern as such remains.

    • The language feature isn't "passing and returning functions" but "loose coupling." Lambdas and Functors are just a way to represent that in OOP languages that care more about inheritance than about messaging.

    • A lot of patterns have become frameworks, or language features, yes. It's just paving cowpaths.

  • Design Patterns is more like the Human Factors and Ergonomics Handbook.

    You can have your building engineered, in which case building walls out of 2x6's 16 inches on center is not off the table, but neither is a mortise and tenon timber frame with partition walls. In that paradigm, the code tries not to be descriptive of an exact technique but only gives you criteria to satisfy. For example you could run all of your electrical wiring on the outside of the walls or on the outside of the building, and you could use ramps instead of staircases. It only talks about ingress and egress for fire safety, and it explains how you're supposed to encase wires, or if wires are not encased it describes the way the wiring must be sheathed to protect the occupants.

    You can heat your house entirely with an open fire, and the code speaks to how to do that safely. So it's unlike "design patterns" in a lot of ways in that the code tries to accommodate the kinds of buildings we try to build and the ways in which we modify buildings because that's easier than saying "these are all the allowed ways of building an entry staircase." Design Patterns are more in the latter category.

    • I disagree with that take. Design patterns are a language for (= give standard names to) patterns that tend to repeatedly occur in code, so that we can efficiently communicate about them. Programmers working in the respective contexts tend to reinvent them sooner or later if they don’t know them already, so it makes sense to circulate the knowledge about them. But that doesn’t mean that they are prescriptive.

      1 reply →

OOP pattern were useful for people stuck in a pure OOP language (say Java 1.4) And needed to make something understandable. Today, when many languages, including Java, have reasonable functional programming support, a large percentage of the patterns are over complicated. Just look at the list, and see how many can be replaced with less boilerplate by passing a function, doing some currying, or both.

  • That doesn't replace the pattern, it just does the pattern by a different name. Design Patterns was never about OOP - the publisher added OO to the title because that was the fad at the time, but the patterns happen in other systems as well, they are just implemented differently.

I agree. Mostly they are copes for lack of first-class functions and multiple dispatch. Go through GoF and you will see this is the case for 80% of the patterns.

OOP has no firm theoretical foundation, unlike FP which is rooted in the formalisms of mathematics.

  • Ok, I'm in an argumentative mood, and I think this is more true than not.

    The first theoretical foundation of OOP is structural induction. If you design a class such that (1) the constructor enforces an invariant and (2) every public method maintains that invariant, then by induction it holds all the time. The access modifiers on methods help formalise and enforce that. You can do something similar in a functional language, or even in C if you're disciplined (especially with pointers), but it was an explicit design goal of the C++/Java/C# strand of OOP to anchor that in the language.

    The second theoretical foundation is subtyping or Liskov substitution, a bit of simple category theory - which gets you things like contravariance on return types and various calculi depending on how your generics work. Unfortunately the C++ people decided to implement the idea with subclassing which turned out to be a mess, whereas interface subtyping gets you what you probably wanted in the first place, and still gives you formalisms like Array[T] <= Iterable[S] for any S >= T (or even X[T] <= Y[S] for S >= T and X[_] <= Y[_] if you define subtyping on functors). In Java nowadays you have a Consumer<T> that acts as a (side-effectful) function (T => void) but composes with a Consumer<? super T> to get the type system right [1].

    Whether most Java/OOP programmers realise the second point is another question.

    [1] https://docs.oracle.com/en/java/javase/21/docs/api/java.base...

Why is OOP lumped with Clean Code? Objects are useful for managing complex states and relationships. They are complementary, not mutually exclusive, to procedural and functional programming.

  • Usually when people refer to OOP they don't mean encapsulation, although that's the core tenant of OOP. Encapsulation, private and public etc is a given. Usually they're talking about the other OOP stuff, like inheritance. Inheritance is pretty much bad and is the wrong abstraction for 90% of stuff.

When applied without thinking about why. Yes.

Except dependency injection. I really can’t imagine why you’d ever not use that. I suppose it’s possible to overuse, but you’d still have better code than without. Certainly more testable code.

  • Because code becomes harder to understand.

    With direct dependencies, if you are trying to understand some code that calls some function and what it does exactly isn't completely obvious, you can press a button to go to it, understand it, and come back.

    With dependency injection it depends on what is going to be inserted during runtime, so you can't.

    • If you can press a button to understand what is going on, "it’s possible to overuse" most definitely applies. Dependency injection, as the name implies, is for dealing with dependencies — things that you cannot observe until runtime.

      Hence the benefit to testing; allowing you to inject a deterministic implementation while under test.

  • Unless you mean just regular constructor parameters, dependency injection in the sense of a runtime dependency injection framework is the one thing I try to avoid like the plague.

    • That is called a "DI Container", and usually manages the objects and order of instantiation etc.

      Dependency injection simply means to take objects as parameters, and not instantiate them themselves (which causes "Inversion of Control" also commonly mentioned when talking about DI). DI Containers just makes the managing of objects easier.

      Avoiding it like a plague seems excessive, did you have a bad experience with them?

      1 reply →

Most of OOP and design patterns was yet another attempt to make it possible for lower-ability (i.e. cheaper) developers to be productive. Just like dimensional lumber and standards like "wall studs are spaced 16 inches on center" made it possible for a lower-ability carpenter to frame a house and have everything fit together properly. Though in the latter case, it actually was successful.

  • Nah, the engineering standards like that generally make everyone's job easier; the "pro" carpenter will save just as much time as the newbie, hell maybe more.

    Design patents are more of "you need to build house with this exact room layout" than "the materials and ways to put them together are standarized"

  • There's a strong element of that, but there's more to it. It is to the advantage of management that even their experienced developers all speak the same design language, if only because this makes any individual developer easier to replace. Corps don't want a situation where the whole company is hanging off one brilliant programmer's completely impenetrable code. TempleOS is awesome, but not for businesses.

I worked with a junior dev who suddenly got really excited about Clean Code. Every example he brought up left me feeling that there was a kernel of good advice, but the book wanted you to take it to such an extreme that it would result in shitty code.

  • > there was a kernel of good advice, but the book wanted you to take it to such an extreme that it would result in shitty code

    I see you're familiar with Uncle Bob's handiwork

  • There is now a second edition of that book which has supposedly been rewritten to fix that.

I think you can safely omit 'maybe'. OOP is harder and requires more design experience to achieve good results than functional programming. I welcome you to look at OOP code from people who don't get the patterns.

OOP can be wonderful, but the people who aren't able to step up a level in conceptual abstraction should really not touch it. Remember, for many years languages like Java didn't have any concept of lambda's and higher order functions, so design patterns were essential for elegant solutions. As they say, a design pattern is a symptom of the language being not expressive enough. In other words, many design patterns in OOP languages express the same thing as first-class language features in the functional paradigm would do, Visitor vs fold for instance.

I think it's more the idea that if using a pattern is good then using all of them at once is even better.

I believe it's more like formal letter, or prefilled form where you only fill data when required. It actually can be useful.

> but I believe a lot of OOP/Clean Code patterns are the software equivalent of corporate BS.

They're the corporate equivalent of USSR soviet style conformism, when everyone had to call each other comrade and refusal to do that had repercussions.

Similarly, if you say you refuse to follow the Agile/Scrum manifesto or clean code practices, you get ousted, as that's Haram/not-Kosher in this racket.

I still wonder how Valve manage to ship Half Life without Agile or clean code practices.