With 20 years of experience as a software engineer moving through the stack and using a lot of different languages throughout the years, I've seen a lot of good and conversely poor development implementations. I've also had the privilege of being able to work with languages (like C#) that placed a heavy emphasis as a community in enterprise development around the mindset of using patterns and practices to make code more readable, reusable, and easier to communicate redundant and repeatable ways of creating applications and services. This has helped me immensely and provided me with a mindset on how to think and truly engineer a well built solution.
The priority of pattern thinking is missing
Has it always been this way?
This isn't to say there has never been a focus on pattern for front-end code. Industry leaders such as John Papa were advocating for the module and reveling module patterns with ES5 code years ago, and even today alongside Dan Wahlin to carry the flag for architecting Angular apps and having a mindset for patterns and practices. Therefore a voice does exist from advocates for sound and well written code, but overall I just don't see the concrete evidence as much as I did when working with server-side code in C#/.NET.
A high-level use case
Let me cherry pick a scenario I see more often than not in Angular. At a high-level speaking broadly, most modern Angular code has a thin service code file that makes an API call, and immediately returns an Observable with the result. The real result is that data is consumed and subscribed to in the component, and all presentation logic, data massaging, and appropriate (or sometimes inappropriate because it's IP) business logic is all done in said component. The result? A massive multi-thousand line big-ball-of-mud that's out of control and really difficult to maintain. It didn't start that way, right? The original MVP implementation was just a simple return and binding of data. However like any software that evolves, so does the need for more code and the initial pattern set forth, is scaled good or bad. In this case bad, and the component is out of control.
What if though something like the Command Pattern (or ideas from it) had be used from the inception? The component only acts as an air-traffic controller of sorts; it doesn't really know how to do anything except direct the traffic. It assembles all needed information, builds up and executes the command on the service where the real work happens (in 1..n services). This pattern also lends itself to creating immutable models in the component and they are only ever changed in the service. The service streams the data from an Observable, and all the component does (with minor exceptions) is bind to the new data. This is a much cleaner approach and a highly repeatable pattern for any experience level. Even if this particular approach seems heavy as you're building a smaller application, knowing patterns and their purpose for use will still lend your design ideas about how to better implement and organize your code.
In contrast to OO patterns and practices but still with critical and planned thinking at the forefront, we could also use a functional programming paradigm (and patterns specific to FP), and leverage things like pure functions to avoid side effects with a goal of consistency and having more readable and logical implementation. Any of these options are better than the absence of any plan, which results in a poor implementation that's prone to bugs and being difficult to maintain.
In the end we didn't complicate the code, we just implemented it differently for the major gain or readability, reusability, and testability. I liken it to these images I used over 10 years ago when talking about patterns and practices in C#. Both containers below have the same content, but which one would you rather grab to get what you need? The answer is simple; the one that is organized.
The plan forward
The question is how do we learn about these patterns and when to apply them in our code? Well the answer is a combination of getting educated on well known patterns and gaining experience using them. It is however an art, and there is a balance to be had on how to use them most effectively. The cynics hear 'patterns' and sometimes get scared off saying, "that will overcomplicate the code!" There are times I agree. How can I spot the difference? Experience. If you don't have any, learn from others. One of my favorite words in the industry is 'pragmatic.' The ability to know balance in code and when and how to use powerful patterns to aid not hinder code. If the two ends of the spectrum are anarchy and over-architecture we want to be somewhere close to the middle. The problem is in my experience, we're too often close to the anarchy-style of implementation on the web client. I think the crazy saving grace that we back into is that since web client frameworks and libraries have only a 2-4 year tenure on average before some major overhaul, all this bad code is made obsolete before it really begins to stink things up. However during that time period it would behoove us to write code that is implemented with better patterns and practices to help extend the life and make the journey a whole lot easier.