One of my favourite questions is definitely: “What is Quality to you?”. I remember being asked this in job interviews — unfortunately, the job search market turns what should be a rich introspection exercise to weigh your true values into out-of-the-box empty responses using as many buzzwords as possible.
Thinking about work, I prefer not to focus on practices too much and instead put more emphasis on principles. A good technical team should have a good knowledge base to determine what the best practices are to accomplish their goals. It’s leadership’s responsibility to communicate principles that will be used to measure success.
It’s important to communicate your quality principles clearly, both in interviews and when you’re working in a team. Your team members have to develop a clear understanding of what your quality expectations are if you expect them to keep their work at that level.
None of this is new. The industry has kept these flags high for ages now. Still, it seems that sometimes teams don’t understand how to bring those concepts down to earth. Turning them into signals that you can use is essential to guarantee a team is aligned.
When I think about quality, these are my 3 driving principles to achieve great results while sowing all you need to reap incredible speed and superior product value in the future:
Drive Through Value
Every change in your codebase has to have a reason, a clear delivered value. Otherwise, why would you do that stuff?
Value can mean a lot of things. It can be product value that moves metrics in ways that you’re interested in, like shipping a new feature or updating the behaviour of part of your system to align with the new definition of right.
Value can also be delivering steps towards a state where these more “measurable” value efforts can be done more easily. That’s where refactoring comes to life: making things easier to change, enabling options like Kent Beck likes to say. Even renaming a file should be aligned with some value: “Removing the need to read file content to understand its responsibility improves maintainability.”
YAGNI code falls out of this section. Why would you put code there that’s not serving any value, or won’t serve any future value in the planned future? Would you install a dog water bowl in your home if you don’t have a dog and you don’t plan to have a dog in the near future?
Understand your observable behaviour
More often than not, I find myself working on legacy codebases that have no definition of what to expect from them.
Tons and tons of code that have no clear behaviour. You’d have to go through hundreds of lines just to get a gist of what is expected from it.
Use unit tests! Think about your future self and how you’ll feel when something is behaving in a certain way and you don’t know if that’s expected or not.
Unit tests are there for that reason specifically. They allow you to write a clear contract in code for what your code is meant to do — and what it isn’t.
When in a greenfield project, use TDD to cover all your observable behaviour exhaustively. You’re in a great position that’s not the most common one: you’re working alongside product experts to build something NEW. Your role as an engineer is to understand the behaviour each part of your system should have, document it properly, and honour those contracts.
Working with legacy code can be challenging. I stand my ground on the use of exhaustive characterization tests to document the current behaviour of systems before working on them. AI is a great tool for that nowadays. It can help you write tests faster than ever. But don’t be naive! Make sure you check that whatever LLMs give you back makes sense.
Characterization tests focus on documenting the current behaviour, whether it’s actually correct or not. You don’t care too much about the correctness of that behaviour – you’re probably just refactoring at this point – but this is a good moment to identify any fishy parts of your system and start gathering more information from product owners. Someone must be responsible for determining what is good or bad behaviour in the system.
Do one thing, and do it right
This “do one thing” principle applies not only to your code, but also to the way you’re facing your work.
If you’re working on extracting some spaghetti code from a legacy codebase with the goal of refactoring and fixing some wrong behaviour, start by only extracting the code! Don’t try to refactor as you go! Break down your goal into the smallest steps possible. Make it simple for you to complete a task in a few hours, not more.
This will reduce your mental burden greatly. You don’t have to keep all that information in your head at all times. Whenever you’re driving somewhere for holidays, you focus on what’s in front of you, nothing else. “Will I have to buy toilet paper when I get there?” — Not the time to think about it.
Keeping the metaphor of driving, keep your eyes on the road in front of you at all times. If you’re pairing, it’s your navigator’s responsibility to guide you on what’s next once you get to that crossroads. You don’t have to think about it (I know this is confusing in today’s world where every driver is constantly stimulated by GPS screens telling them what to do next. In an ideal world, drivers should focus on driving and navigators on telling them where to go next).
Last thoughts
If I have to choose one principle to guide all my decisions, it must be Communication.
Write PRs that communicate the value you’re trying to deliver. Write your code to communicate its responsibility and its technical decisions clearly. Write tests to communicate expected behaviour clearly. In today’s world, communication is the main enabler of both human and machine interactions — and luckily, it’s a purely human trait. Cultivate it.
