Architecture, design patterns, why and why not?
July 23, 2023
This will be an opinionated piece. To the point that I am thinking about and not sure where I stand in the equation of all things. And that is with the evolution of our architecture that goes towards minimization of responsibility in the context, do we still have a need for complexity introduced by design patterns? Do they still fit somewhere in this fast-evolving world of micro/nano/pico services?
Wow, this is gonna be interesting. Let me see how you gonna pull yourself out of it. I won’t, simply as I am also struggling with it myself. With every new piece of code, I put in some new service. So hence me throwing it out there, to see how smarter people are than I think. Does all the complexity makes sense anymore, instead of making code more understandable and easier for maintenance? Don’t get me wrong, I will still reach a pattern if I see a purpose for it. While on the other hand, I review and still see a number of code bases that start out with some pattern. Without even verifying that there is a problem in it, where they’re a solution. It’s ingrained into us from the “old” days. Wait, what? Yeah, I remember the days when interviews were all about design patterns. Do you know their classifications? Do you know this or that one? Some fond memories there.
Now, the new age is here. Being here for some time. At least in the software development sense of time. Or how we estimate our stories is another dinosaur of old, that probably deserves an asteroid more than this. Agile. But the topic for another time.
So how do you justify yourself using some pattern from the get-go? How do you balance it against that mystical animal we’re chasing in our code? Code readability. Or even bigger unicorn. Maintainability. Or that is how you ended up there in the first place? Full rewrite of a previous attempt to do something similar that failed spectacularly? And now the only solution is just a new scratch and start over? Without taking the learnings of original failure into consideration? What could possibly go wrong? Saw this numerous times and was an offending party myself. It is so easy to reach that “old” way of thinking. While we should always start, in my opinion, with the good old one. KISS. Lives could be also simplified with another one. YAGNI. Especially if you’re building something for a manager that thinks nine women can deliver a baby in a month. Like yesterday.
I am starting to consider that time spent on good architecture from the get-go is a bad idea. Especially on greenfield projects. As, in my experience, we have no clue what tomorrow is bringing. And if we set something hard in stone, we soon end up cursing ourselves. And try explaining that to a stakeholder. How you need to go back to scratch as we limited our experimentation process way too soon. I would really like to hear how that justification goes.
Does that mean that I’m fully against using design patterns and architecture? Heck no, I will still refactor toward one when I see one. So hence that split-brain situation from the start of the post. I still will gladly introduce one of my trusty ones in many code bases, Chain of Responsibility. It reduces cyclomatic complexity and makes code easier to understand (matter of opinion). Maintainability is a tricky one with every design pattern but I would say, as well more maintainable. If you’re not hiding its complexity with some library. Then, heck no. Write a nice function that registers that chain at any time over some additional dependency that you will need to own. But we’re not here to talk about concrete patterns. As said, I do strongly believe they have their purpose, still. Even in the world of ChatGPT, I still believe that we will care about our code. Especially if we believe that its expiry date is not the same as the time of shipping it to production. In those cases, I would really have a hard time justifying my time invested. If it is there to serve a purpose of ephemeral nature.
How to know when that is a hard one? Especially as we work with managers where often you will encounter: It works, why change it? When your code is out of that proof of concept phase, that is. And with every new feature, you’re starting to see a use-case of introducing <insert pattern name here>. That is another topic that I tackled in the past. So with that in mind, you will recognize it when that time comes.
My rule of thumb is becoming making it work according to the requirements-driven development. As your code will already be in a nice structure and readable. With a really great set of tests, that helped you already write a nice API that you will now evolve into the next stage. I choose you, Pikachu. With them behind you, you will have confidence that refactoring will not come with some nasty side effects. And angry calls in the middle of the night from your incident commander. I think with this approach you set yourself for a good iterative evolution of your architecture. You spent time ahead to make it readable and maintainable. Now it is time to move that to the next level. So zero downsides, all benefits. This is my take on how to approach the code that will not result in waste. Feel free to define one for yourself.
I am starting to see while writing this, where my opening question is leaning towards. I will ride this wave and see where it leads me. If I would work on some of my old projects, monoliths of millions of lines of code, I think would probably still keep this mental shift I came to have. Would be keen on refactoring towards patterns (as most of those solutions are littered with them) just to keep up with the standards, but wouldn’t start with them. Because of the reasons mentioned in the previous paragraph. Cognitive load when adding a new feature to the existing code base is already high. A bit of practicality with good practices (again, previous paragraphs), can reduce that. When you have a maintainable and working solution, with a nice code base, you can queue the next one. Design pattern. That way at least you don’t keep in mind what if and how. You have them, now they will just change the shape a bit and hopefully improve the status quo.
That is why the title also contains the architecture in the title. As there are some architectures out there, pre-chosen, that may “dictate” what you will need to do. Those discussions can be left for PRs. Where I would still argue that an iterative approach is the way to go. If you say you’re agile, then at least try doing it as intended. Then again, I lost from time to time the battle. It is company-wide standards, we did it like this for years, we know it is broken but just do it… Ah, the memories.
So thank you for rubber-ducking my thoughts, let us see if this is a way to go.