For as long as I can remember, the KISS (Keep It Simple Stupid) principle has been around to remind developers and businesses to approach a given problem with the easiest (or most simple) solution as possible.
Which basically means that the complexity of a software design should always be correlated to the complexity of the problem it tries to solve.
So, to put it simple: simple problem, simple solution, complex problem, complex solution..
The art of simplicity is a puzzle of complexity. -Douglas Horton
I've been a software architect for a couple of years now. I've solved very big "problems" and I've solved small ones. One of the many things I've learned in my career is that Software Design has to evolve, and that you have to be able to evaluate how changes impact the important characteristics of the architecture and prevent degradation of those characteristics over time. These changes can include changes in the technical landscape, changes in your team, changes in requirements, etc.
In the last couple of years (probably even more so since the first major release of NodeJS in 2009) new technology stacks have been introduced at lightning speed in the .NET development community. CQS, CQRS, AngularJS, AngularX (2,3,4,5,6), React, VueJS, VueX, .NET Core, JS Functional Programming, CLI tooling, bower (or is it Yarn now), NPM, event sourcing, docker, parallel computing, AI and even Quantum Computing.. The list goes on and on and on.
We currently have the possibility to solve the most complex problems with the most advanced solutions. But it is up to you to keep up. And in my opinion, here lies the biggest problem.
Challenges of an always changing Development Landscape
As an architect you have to evolve alongside the changes of your technical landscape. You've got to keep up, so that you can provide the best and most up-to-date solution for the problem you are trying to solve.
But that is not always easy. With an always changing landscape it is becoming very hard -maybe even impossible- to stay on top of things. When you start learning a new technology, language or framework today; it is not unlikely that that technology will become outdated, upgraded or obsolete before you get the chance to master it. Staying on top of things is a bit like a hypothetical dog, trying to catch its own tail.
The complexity of a given problem is just one of the factors (let's call them dimensions) that you take into account when deciding on your software design. The team of developers you have to work with is an equally important dimension (are they familiar with web technologies, do they work remotely, which software methodologies do they use, etc.). Another important dimension is deciding where the "heart" of your software design lies, and how and what you will use to test its evolution.
And maybe most important of all, which technologies will you incorporate. Which design patterns will you choose? Does the design need to be reusable? How long will the design be used?
You'll have to get it just right, iterate over your ideas and make changes accordingly.
It seems -in Belgium at least- that one of those practical lesson learned after years of being an application architect somehow got forgotten over the years. You've got to keep things suited for the problem at hand. Never forget that adding complexity is always a vicious circle. Just like worrying, where you eventually find that you worry because you worry, because you worry. Adding complexity is much like that. Which is neither good or bad, it just is; but you have to know what you are doing, and what the consequences of your actions are. When you start adding complexity just for the sake of adding complexity, things tend to get out of control, fast.
Let's try a use case: Say we have a web application that at a certain point in time has to deal with more load than it can handle. The application uses an in-memory cache to avoid round trips to the Database Server. Because of the new load (= new complexity) we have to change our caching mechanism to a distributed cache so that we can scale out (deploy your app on multiple servers). So, we add complexity to incorporate the new complexity!
Because we are now using a distributed cache, all our developers will also need access to a local distributed cache. We will need to add it into our development cycle, we'll have to add automation, add integration tests, teach our developers what they need to know and setup distributed caches for our different environments.
A fairly simple change in complexity is like throwing pebble in a pool. Each ripple you create is like a new layer of complexity, spanning not only to the development itself, but across the whole development and deployment process (from development into production).
Always remember, the most complex -or coolest- solution is not always the right solution. Just because you can, does not mean you should! There are many dimensions that drive software designs, technology stack is just one of them.
If you are using something just to use something (because it's cool, because it's bleeding edge), which does not add the right amount of business value or is not suited for your business case or architectural domains; sorry, in my opinion, you are doing it wrong.
Let me be clear, I'm not saying I'm against changing or upgrading technology stacks or adding complexity in your software design. I am not. As a community we need to strive to move forward and it truly is an amazing time to be a .NET developer. But if you've decided to use a full blown CQRS solution with asynchronous event-sourcing, with an Angular - Redux front-end; to allow your users to perform CRUD operations on a couple of lists (real-life example); sorry, you've over engineered the sh*t out of your application.
In Belgium a lot of companies are choosing Angular as their main front-end technology. I've not hidden the fact that I'm not a fan of AngularX. The -I'm assuming- breaking changes with every release (which is what, every two months or so?), the level of complexity, the CLI, the opinionated dependency on TypeScript; everything about it is just something I'm not a fan of. A front-end application, for me, should be kept as simple as possible, to view and edit your data as simple as possible.
And if your team consists of only seniors, I am inclined to say, go for it. Go wild! But I hope you realize how unrealistic this is. Teams change, juniors and/or mediors will work on your code. And with framework as complex as AngularX is, especially in combination with Redux or Flux, maintaining your application will be challenging to say the least..
On docker, Kubernetes and the "Dev-ops Culture"
With the rise of .NET Core, the incorporation of Microservices was fully introduced into the .NET world. With Microservices came CQRS and Event Sourcing. With Microservices came Docker, container-based application development, better or more consistent DDD implementations, and Linux deployments, and eventually maintainability using Kubernetes. Each technology is cool, "new" and awesome to create.
But to maintain.. Oh dear. With a system wide failure using Async CQRS you are not only risking downtime; you're risking data corruption throughout your system. If it's not implemented correctly, a simple bug can have truly catastrophic results..
As I talk to software engineers implementing such complex and vast applications I tend to challenge them om maintainability. Because that's the Achilles heel, that's where everything could go entirely wrong. And when I talk to them about the management of these containerized applications, they all get that wild stare in their eyes. Knowing of Kubernetes, but never worked with.. Thinking about massive system failure and how it might impact their data. Getting notified if a container goes down, or even worse, the (shared) data container goes corrupt. They simply have no idea! They are developing applications of which they have no clue, not even if their life depended on it, what to do with the system should it go down.
In their defense, you could argue that they should not need to know these things. That they should have the shared responsibility with a Dev-ops team specialized in their field, so they can assist the developer if need be. So he/she can focus on development, not on maintaining. But this shared responsibility is slowly, but surely, fading away.
There used to be a time where development teams worked with Dev-ops teams to maintain production applications. But that differentiation is more and more a thing of a past. We don't talk about Dev-ops teams any more, we talk about a "Dev-ops" culture. Where developers should learn how to deploy, manage and maintain production applications as well. Which isn't all that strange when you look at it. You cannot expect a member of a Dev-ops team to maintain an application where expert knowledge of every container is required. You simply can't. But putting all the responsibility with the developer is not a good idea as well.. The design is just too complex, the lines between the responsibilities and functionalities of Dev-ops and programmers have faded to much.
This might be a more "negative" read for you as I've tried to put my frustrations about the roller-coaster of technological advances in our world into words. You might get the wrong idea. I really am not against changing and evolving, I love learning new things, I love guiding a client into this new era we are now in as software developers.
But I also see it as it sometimes is. As a train riding with enormous speed, going faster with each iteration, each new framework, each new technology stack. And, in Belgium at least, I see the point where that train could possibly derail. Everything is simply going too fast.
If you are an architect, or software engineer, I urge you to take responsible choices. Take decisions, iterate on those decisions, and adapt your visions and architectural dimensions as you write your code. Incorporate new ideas and technologies if you need them, not because someone told you they are cool and everyone is doing it nowadays. And always, Keep It (as) Simple (as possible).