I saw two separate tweets today that were examples of the same issue in software these days: Choosing the solution first, then retrofitting the problem to it.

We’ll go over both examples now. Buckle up.

Choosing Kubernetes Because Everyone Else Does

Kubernetes solves a very specific problem in a very specific way: You want to add a separate abstraction on top of the servers to orchestrate your containerised apps. Rather than maintaining their servers, the devs maintain their container images. It solves the complexity of shipping software by adding another complexity: The abstracted infrastructure. The need to maintain an infrastructure doesn’t go away, it just draws a curtain between the apps and the infra. You still have to maintain the infrastructure, now you have to maintain the Kubernetes cluster as well.

If your app isn’t big and complicated enough to warrant that complexity, or you don’t have the resources to maintain a cluster, you’re in trouble. You’ve just added another layer that needs maintaining without solving your problem.

If your app can live on serverless and doesn’t have a vendor limitation or a specific cluster requirement, why bother? Most cloud vendors allow you to run a containerised app without Kubernetes (e.g. Azure App Service can run containerised web apps and API apps). Or if you don’t even need a container because your app is so small, why not run it on AWS Lambda or Azure Functions? Why not take on the cost of moving to somewhere specific one day, instead of the cost of possibly moving anywhere?

Choose a solution that would fit your problem. If you don’t have multiple teams working on your CRUD-based app, go with a monolith. Take on the cost of conflicting changes over the cost of over-architecture. Pick real value over a virtual one.

Over-complicating Your Solution From The Get-Go

The Clean & Onion Architecture models are great, but there’s nothing new there. For many years, a lot of the apps were haunted by the “Three-Tier Architecture” and created one-liner service Facade layers over Repositories to serve the API or UI layers. It’s the same shit all over again: Defining your layers before defining your problem will only cause you more problems.

Any new layer of abstraction is a layer that needs to be maintained. SOLID means nothing if you also don’t implement KISS (Keep It Stupid-Simple). You don’t need to make everything extendable for the sake of complying with the Open-Closed Principle.

Let your application requirements drive the layers. If your requirements are complicated enough to use the Clean Architecture, by all means, use it. But don't start with it just because "you may need those layers someday". Your guide is your requirement list, not your architecture.

I like the tweet below from David Whitney (@david_whitney):

The complexity of your software should be less than the complexity of your problem space, and never greater.
If it is? You caused more problems than you solved.

Conclusion

There’s a reason we have a list of Anti-Patterns. Choosing a predefined solution before understanding the problem is the perfect definition of the Golden Hammer. There’s a saying in Turkish: Don’t order pants for your unborn baby.

Remember: Your job is to solve problems, not cause them. Be mindful that your actions can also contribute to the problems, even though your intentions aren’t to do so.