> I hate this requirement (and other similar ones). They force the crystallisation of architecture long before the concepts they model have them selves had time to fully crystallise
I'm not a fan of single-class-per-file as a rigid rule, but it's not my effect that it has any effect on architecture. Adding a file with a new class isn't substantially greater overhead than adding a class in an existing file, and moving stuff between smaller files is, if anything, easier than moving it around within a single monster file.
> If you can't see how utterly stupid builder pattern is for this case... well... cognitive bias.
You cannot win against the prevailing wisdom. Try having a public mutable field on a class, and watch people lose their ever-loving minds. But why do we have more than half of the classes' exposed with getters and setters? Mmmmfh.
I think the problem is that it is really hard to reason about software in the large. So we reason about software in smaller and smaller granularity, e.g. classes. And then we want to load down every single class with armor as if it needs to be protected against the wild hordes of unwashed masses who will rampantly mutate it. But most classes naturally fit into a larger unit, a cohort of classes, and they are coupled with each other, and they mutually share state. You can't understand the whole thing by staring at a single class, anyway. But it feels like we are doing the right thing because we are told every class needs armor. Nevermind that now every class is 3x bigger, and so the whole thing is 3x bigger, which makes it 9x harder to fit in your head! Uggh!
> Then don’t. Or only do it after everything is done and you have explored the domain.
I think this really hits the nail on the head. Classes are the ultimate DRY tool but it seems like much of our industry is realizing that it's rare for any project outside of small self-contained libraries to reach the point where you know enough about your problem domain to ever start meaningfully factoring code out like this.
Instead having "here are the different shapes of state you might see floating around" and "here are some functions that can do things with certain shapes" becomes really powerful when you need to be nimble with features/requirements. Encapsulation is beautiful when the scope is specified completely up front but it I've found it leads to more complicated code and mental overhead because you create a world where the least cost path to doing what you want does a run around with types.
> At the time, fresh out of Uni with a head full of design patterns, I thought this was just how enterprise code was meant to be structured!
Ugh... and that's the flaw in teaching design patterns.
Don't worry, that happened to me too, though I didn't go to a university.
Design patterns are an advanced tool. If you're learning to code, design patterns are mostly non-applicable to the kinds of problems that newbs are solving. Design patterns are meant for specific problems, but somehow we end up believing they're the end-all-be-all and that we must be using some design pattern in our work. If you have design patterns pounded into your head, and you believe the only design patterns that exist are the one's someone has already named, then not using a particular design pattern can seem like chaos even when it's not.
I wish we'd stop teaching that shit. If you've been coding long enough and faced complex enough problems, you'll either come to embrace some design patterns or you won't. Otherwise they'll likely just be misapplied.
OOP fits this view of mine as well. In general, I don't think most programmers need to be aware off OOP principles because they will almost certainly misuse them. And to what end? Several single purpose classes and "tiny functions" scattered across multiple files that others are now forced to jump between.
>When I start a new C# project, first 40 minutes I am mindlessly writing a scaffolding of classes, data structures and helper functions that I will need from the beginning.
I share this sentiment but the recently announced changes to file level namespaces, global imports, top level definitions etc. have the potential to eliminate a lot of pointless boilerplate.
Now the structural patterns - those are self inflicted - you pay that cost to keep the thing consistent and maintainable down the line - but people often can't evaluate when it's not worth it (and often even don't understand what they are try to achieve but instead treat it like boilerplate that has to be present)
> what they couldn't have done, probably, would have been cleanly implementing that on top of class-based components without then having to tell their large and influential functions-are-the-only-way constituency that they'd need to write a lot more classes in the future.
They were saying that for a long time before hooks when the only way they had it implemented at all was for classes, and functional components were usable for much narrower use cases. I suppose, if they could have done it cleanly with classes, they would have, whatever that meant saying to those downstream who aesthetically preferred functions. I don't know if it inherently worked better with functions, if the core devs themselves are just more proficient in that style, or if it was just a confluence of experience and the timing that the push to improve functional components occurred, but in the end functional components are just much cleaner.
> I don't really understand how important these design patterns are because in the programs I write, I usually write the classes and call them in runtime myself.
One of the biggest learning moments in my work has been collaborating my past self. You need to have stepped away from some piece of code for a while to get this. If the code is not well organized you will think an alien wrote it.
It's only over time you realize that "it works because I know how to use it" is actually a problem. Over time you also figure out how to write the code in a way where you aren't surprised at your own decisions.
> if I solve the reuse issues with some composition and the is-a problem with an interface.
I'm not sure how that's better -- you're just implementing inheritance with more steps.
> is both very fragile and usually very short lived.
It is and I know you'd make that point but it's better to be fragile and short lived than completely impossible. I have a least one of these hacks that was completely necessary that has been in place for years.
> When, not if, things break, there's a large benefit to having all of the logic contained to a few heavy-lifter classes that are contain bespoke logic and are fit-for-purpose.
Things break in cycles. You'll have worked around a first wave and be happy that you didn't abstract your code too much as you can just fix one side of your logic very locally. It also means you didn't touch the other sides that were not directly affected, but probably needed a fix in a slightly different but overall similar way.
So you'll see your code instances all break a way or another and fix them one by one instead of hardening a central piece where you could focus your testing efforts.
Of course it's a topic that needs nuance, but if you identify a piece of code as duplicate, there will be no free lunch. Either you pay upfront the effort of abstracting, or you pay down the line the local fixes, but there's not one approach or the other that will be fundamentally wrong, I see it as a bet that pays off or not.
> At the point where you're being very choosy about the access modifiers on your classes, you probably thought about icon assets already.
You'd think, but many of the most popular apps accidentally ship these all the time. I think another comment mentioned that much of the code size seems to be coming from a code generation framework.
>Most frameworks nowadays has [sic] become so stupidly complex that they almost require more time to understand that the underlying programming language.
I mean they do mention that just after, and I agree. Often the frameworks take away a few hours of boilerplate work in the beginning in favor of weeks and months of learning and issues.
>I think hooks is what finally pushed me over the edge towards saying to myself, enough with this insanity.
Did you actually build anything with Hooks? I was pretty turned off by it at first sight as well. It doesn't jive with the sensibilities of a class based OOP developer's natural instincts. But once you start using pure functional components it all makes sense and your code becomes so much cleaner and easier to reason about. No more component lifecycles and massive class files.
> No, that sounds like a tangental problem born of inexperienced developers and/or poor communication.
I've yet to see a single project to not devolve into a mess of class names. Not only that, but a mess of class names with significantly repeating boilerplate, intimately tied to specific elements.
> The idea that one class changing won’t change others is a pipe dream
Reality is never black and white but that's a good principle for good software design and worth at least aiming for.
If anything that has been adopted and generalised in microservices, etc. because encapsulation, modularity, design by contract all hinge of this same idea, which is powerful and useful.
That's why when discussing any "principles" the important is to get the core idea behind them instead of sticking to the letter and discarding them as useless.
> Then when you do refactor/redesign, the language you're using to create your classes isn't arbitrary, it's well-thought out and reflective of something that is needed.
This is how I generally like to design systems. But it also sounds more like DDD than a DSL; especially the concept of ubiquitous language. I prefer OOP to DSLs, mostly because OOP composes well since it's a first-class citizen in the paradigm.
> There are so many programming languages, frameworks out there that are constantly evolving and companies have different demands.
An idea that I have thought of is to standardize on some programming language/frameworks/demands that are known to be supported for a very long time (15 years?). If you need something from this buffet, you are fine and have the advantages that this provides. If you have special demands, you are on you own - which is not a problem per se, but as all special demands this simply needs to increased risk or cost.
And they are in the spec. It's too late to be sad about that, people are going to use them and they should.
reply