Friday, December 4, 2020

The Absence of Pattern Thinking for Web Client Code

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

The problem I've seen over the last 5-10 years as a primarily focused JavaScript web client developer is the absence of this type of thinking. So much of the code I see written is just jammed into a file as a means to an end to complete a needed goal. I think unfortunately the explosion in JavaScript development and rapidly created JS developers due to demand has been partly to blame. I also think there has been so much focus on, "which framework/library," that has had a byproduct of having to learn so much just to get code implemented, that pattern thinking is just not at the forefront. Plain and simple there hasn't been time to organize thoughts or thinking around a sound architectural implementation using known patterns when the web client developer is just trying to get their head above water learning the framework, array of JS libraries, state management, CSS, responsive design, offline capabilities, mobile-like features, new work, enhancements, etc.. This in contrast to more stable back end languages that have had similar implementations going on for decades (albeit with new ways to wrap code or deploy; i.e. cloud) where the tenured experience has helped provide an environment where pattern thinking is much more prominent. I know this to be true, because I've been on that side of the fence as well.

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. 

It's time we as a web client community when building enterprise web application push harder to use some forethought into how we implement our code borrowing concepts from known patterns and practices. It's not enough just to cram thousands of lines of code into a monolithic style .js/.ts file. This as an aside is one reason I'm not a huge fan of CSS in JS because it adds to desegregation of code and cramming everything into a single file. I don't consider myself old school to like separation of concerns (SoC), as it's really about organization of thought and practice. Much like the UI web component code we like to implement today with a mindset around segregating code into smaller, cohesive pieces of functionality, we must too apply that same style of thinking to the imperative code we write in JavaScript/TypeScript.

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.