DEV Community

Renato Santos
Renato Santos

Posted on

[en-US] Bad design leads to bad architecture

Obviously, the inverse is also true. The lack of a well-thought architecture leads to friction points, especially when speaking of integration. Add this to a big development team and most surely there will be code-level issues.

Either way, long before we delve into software architecture, we work - and bang our heads a lot - as software developers. If you work in an environment that respects you as an individual, you, as a developer, routinely think about the structure of your code and implement it as you see fit. This is not to say that there will be no coding patterns or practices such as code reviews and the like, but you are responsible for thinking; sometimes you have a good idea, sometimes not so much.

Developers are not - or shouldn't be - robots. Thinking is an extremely important part of a developer's life or of any professional, really. I believe the most efficient way of professional development is to allow the developer to be creative as it is not easy to come up with a good solution to some kind of problem. I always try to allow the developer, especially those who don't have much experience, to implement their ideas so that we can later discuss the reasons behind that idea and come to a conclusion together as to why it is or is not suitable.

Be that as it may, I am not here to talk about the process. I just want to reiterate that while we don't even dream of software architecture, we are already responsible for software design. While being a good developer does not mean that one is or will be a good architect, understanding how to structure an application's code well is certainly of great value.

The problem is that no matter how many years of experience I may have or how many times I work on this kind of activity, thinking about software design is always a hard task. And to those who don't agree: or you are just good at it or our understandings of a good design is quite different.

If you belong to the first group, lucky you! If you fall into the second, just know divergences are normal and this doesn't make me more right or wrong than you - I'd also like to make it clear that I analyze everything from an object-oriented prism and I don't get into talking about functional programming, for example.

A few days ago, I saw a small discussion on LinkedIn about the following illustration:

Image description

All responses, except for one, agreed with the idea presented by the image. According to the self-title of each participant, different levels of seniority were in agreement, from juniors to tech leads. Of course, in practical terms, those who speak up are generally in agreement with the idea and do so as a form of support. Either way, the percentage of people who agree or disagree is irrelevant, the key point is that agreeing with this is not directly linked to experience.

Well... I find this illustration, to say the least, absurd, and I must say that it's actually quite hard to even reason why. What the illustration says, to me, is: "patterns, OO, and abstraction are bad and unnecessarily complex artifacts, program simply".

For me, there is a fundamental flaw in this statement. What the illustration calls "super simple code", I call "simplistic code". Always seek simplicity, but never be simplistic. Simple means being able to filter out unnecessary complexity while simplistic means ignoring fundamental factors of the overall goal.

It doesn't take much to verify that the illustration is flawed and, effectively, simplistic. It ignores all goals of an application, design, or architecture to make a false claim that those with years of experience don't use OO. Admittedly, OO is not the correct paradigm for every situation, but it has absolutely nothing to do with experience.

Phrases like "I might need this later" or "this is what the experts do" are cheap and contentless disqualifications, as they are based on the idea that abstractions are commonly created on a whim.

The fact is, changes are common in the software development world and abstractions are perfect tools for isolating complexity. Design Patterns are not even worth getting into: they are proven solutions to common problems, they are not just some trend among less experienced developers.

I also wonder what the author considers as OO, given the overwhelming majority of Java web applications are not object-oriented at all. The perception I have, however, is that our understandings of what an OO code is are not that far apart.

Correct use of object orientation allows an application to accommodate gradual change and grow sustainably. Unfortunately, it's common to see cases where a service is created and, within a few (or several) months, problems arise: messy code, excessive complexity in making changes, an unnecessarily long time to make a simple change, etc. The interesting thing is that in most cases I've seen this happen, the applications were "simple".

Whenever changing or implementing new functionality, we always have to evaluate the application as a whole and what the insertion of new code entails. New functionality is never just that. It is easy to develop a piece of code that is very focused on delivering the behavior, but the truth is that a change has a much deeper impact on the structure of the code. It is at this time that we must evaluate whether our structures are suitable for the software and it may be that, at this moment, it is necessary to create abstractions, change existing structures, etc. This is being simple, it is knowing what level of complexity is necessary to achieve a goal. Being simple is hard, being simplistic is easy.

Furthermore, we commonly ignore what I call "support code". A domain does not exist in a vacuum, it needs an adequate logic to support it. The support code is not analogous to the concept of "pure fabrication", even more so if you consider that a spring service is the example implementation of "pure fabrication". A fairer comparison, perhaps, is to think of the microservices chassis pattern. Anyway, the structural code doesn't necessarily only support common functionality (logging, tracing, etc).

The result of these design issues is problems in the architecture. The difficulty of creating OO code is often responsible for creating a huge and unnecessary amount of microservices. Since we cannot separate complexity through abstractions, we physically separate complexity into services in search of loose coupling. To me, it's a misguided effort: it leads to exacerbated coupling, albeit masked and often ignored. It also causes an explosion of smaller and less relevant services, and creates bad services such as "entity traps".

Some are a fan of nanoservices and I do not disagree that they make sense for some contexts; I understand that they can be very useful components, especially when we think about an architecture with technical partitioning. My point here is that bad design creates an unnecessary service explosion.

Implementing changes, or rather creating OO code that adequately supports changes, is so difficult that I once heard someone argue that microservices are made to be thrown away. I must admit that, in this case, I can't even reason as to why that would happen. The idea of ​​a disposable service does not enter my mind, a service that is recreated when something goes wrong or when a change is big enough. Not that it doesn't happen, but it's certainly not this mindset that should guide the design of an application. Again, what we see is the creation of services without an adequate level of evaluation and a lack of attention to the whole.

And what is the result of this architecture driven by bad design? This is where the famous distributed monoliths arise. Parts that are excessively dependent on each other, functionality distributed without a guiding foundation, and difficulty to maintain and operate.

Top comments (0)