The Good, the Bad and the Ugly
A lot of developers are not able to distinguish between a good and a bad design and which are the problems of their code. Is your code The…
A lot of developers are not able to distinguish between a good and a bad design and which are the problems of their code. Is your code The good, the Bad or the Ugly?
I have been in a lot of conversations with other developers where they don’t have tools to select between one option or another when a solution is proposed. Even they don’t really understand the problems of their code.
We could start with the 4 key rules of simple design of Kent Beck:
Passes the tests
Reveals intention
No duplication
Fewest elements (remove anything that doesn’t serve the three previous rules)
We could use these 4 rules to define our code as “Good”, “Bad” or “Ugly”. If your code is Good, you and your team probably know more than me about design, Bad and Ugly are the most common states. Bad is a problem but Ugly is good, that means you are in the road of Continuous Improvement. If you are Bad just start changing, because if you hit bottom then your company will be in big troubles.
Is design iterative?
Is it possible to write in your first attempt the code to solve a new feature without bugs?
If you did the same task in the past and you have a great knowledge of the problem, perhaps. This is hard to see, not the norm.
In any other case no.
For this second case, we need to follow these rules to improve design but It’s very hard to write each new line following them. As Kent Beck says:
“Make It Work, Make It Beautiful, Make It Fast”
The first thing to do is solving the problem. This is a must, our code is done to solve a problem. So when developing our first goal is to make our code to work.
In TDD this is done by the first two phases.
Write a test that fails.
Write the minimum code to pass the test.
The second step of TDD forces you to apply the rule of Fewest Elements. Once we are green we “Passes the tests”.
Make it Better and Make it Faster is “refactoring”. And in this block you can iterate to achieve these rules following that order.
Reveals intention: “Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” Martin Fowler.
No duplication: “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system” The Pragmatic Programmer. This means that we need to fix simple duplicity but also Duplication of intention.
The important is the result along the time. TDD will help us to follow it in each commit, but each time we add code we increase the complexity of our system. Sometimes we cannot afford structural changes in the middle of our features, we sometimes take trade-offs that increase our tech debt. How can we manage these situations?.
Let’s talk with Kent Beck again:
We could summarize these ideas with this sentence:
“for each desired change, make the change easy (warning: this may be hard), then make the easy change” Kent Beck.
This is for me the first border between the design of your system and Agile methodologies. My experience says that we cannot afford all the changes we want to introduce in our system in short term.
Software design and Agile
Following Theory of constraints we need to define a buffer before our constraint, that’s the reason why all the Agile methodologies work with a backlog of items (Thinking is the bottleneck).
My point here is that for creating a good software design we need to manage two concepts hard to understand for non devs:
Tech debt (coupling is here)
Bugs
So to be effective refactoring our code, to improve our designs, it’s mandatory to tackle both things. In this article Bugs Vs Features I talked about how we can work with them. But I just wanted to emphasize that your design is also created fixing the problems the actual design has.
Ok, but how Tech debt and bugs can be used to improve my design?. Following Pareto’s rule 80% of bugs come from 20% of our code. As bugs are developers creation we should start pushing to refactor that 20% of our code to make others developers to understand it better and reducing bugs in general. Bugs are easy to find, but what about tech debt how to identify it?.
We need to read our code (don’t trust in architects that don’t develop anything) and follow four rules of simple design:
Is there any part of your code without tests?. I assure you that code without tests is the best way to feed bugs. Probably that part of the code has no tests because the design sucks and is very difficult to write them.
Do you have comments to explain some parts of your code?. That’s technical debt, if you have them is because reading your code is not enough. Those places are great candidates to produce bugs. Do you have units called “utils”, “helpers”, “admins”, “managers”?. Those names are useless, they are smells. Follow them to identify your tech debt and improve your names.
There are only two hard things in Computer Science: cache invalidation and naming things.
— Phil Karlton
Do you have code copied and pasted multiple times to solve the same problem in your code?. High coupling, low cohesion are usually effects of this problem. So you probably need to refactor your code, that’s why you need tests.
Do you have dead code?. Are you trying to solve a problem that doesn’t happen and is not going to appear in the next month?. Remove all that code, avoid YAGNI’s and simplify your design with “Fewest elements”.
We need to put our problems in a privilege position inside the team, because they will act as triggers to improve our design.
Teams and design
You don’t write code alone, your design is the resultant of the knowledge, autonomy and principles of the current and past member of your team/teams in charge of that code. So your design is showing your team, if you want to improve the design you need to learn more things. It’s very hard to create a microservices architecture if no one in your team takes care about coupling.
So we need to improve our skills to improve our design in different directions:
Tech skills
Team skills
When to take decisions
Tech skills
When we talk about tech skills people always think about frameworks, libraries and technologies but as Uncle Bob says tools are not the answer. We need a way to be able to learn things quickly and know how to work with them doing a good job. A good starting point is XP practices:
There are much more things to learn:
Write better tests (TDD, TCR)
SOLID principles
Micro steps
Software architectures (Monoliths, Microservices, SOA, Distributed Systems, etc)
Before focusing on learning technologies without any skill in the above subjects.
Team skills
As we develop inside a team, we need to know how to work as a team. Working as a team usually means that is more important the whole goal than a particular stone on the road. We need to learn which habits processes are going against our objectives, why our pace is not good enough or what can we do as a team to reduce bugs.
Some useful things from XP that will help us:
• Communication: What matters most in team software development is communication. Without communication, you cannot change things because probably you don’t have the power to do it.
• Simplicity: Just solve the problems you have, follow YAGNI principle. But design the system to be easy to change.
• Feedback: This is the tool we need to use to change our system. We need feedback from our stakeholders, clients but also from our code and from our teammates. It’s important to know what to change and which is the better direction to take in our code and in our behaviors.
• Courage: We need to fight against our fears. Fears to change, to talk with a teammate about one part of his job. Fears against changing a running, successful system in production. To be against our tech lead or our management, we need the courage to show our voice and improve the team with our experience.
• Respect: This is very important, you need to respect your teammates and I would say more. You need to be able to trust them for everything related to your team.
Your team should take any decision with the idea of improving your real problems. For example if tech debt is a problem then your team could create Batman to fix them effectively.
When to take decisions
This is long for this article but you can take a look to this other.
As a summary Software Design/Architecture emerges from our code, the next features, our bugs, our tech debt. But we need to listen them and understand the real problems to improve our tech skills to be sure we can afford those problems. But sometimes problems are deep in the team, so we need to work as a team to fix them, without that it’s impossible to have a better design/architecture.