How do you make sure that the software at your company is good? In this post we’ll tell you how one team at Annotell works to continuously improve the code quality and their skills. This is good not only for Annotell as a company leading to a better product, but also makes the working environment better by boosting the developers' confidence and making it easier to onboard new colleagues. Our machine learning engineers share their journey on how to improve the code base by becoming better coders together.
We are a small team of 4 machine learning engineers working at Annotell, a startup that in the last few years has grown massively. Due to this growth, new teams have been formed to more efficiently organize the company. Our team is one of the latest additions, and our mission is to enable effortless development and deployment of machine learning models. Where we previously largely developed models in isolation from production code, we now want to bring them together making it easier to deploy models. This new team structure enabled us to give our attention to parts of the system that desperately needed it.
If you are a professional programmer, the code you write probably exists for the purpose of making money, and that implies a lot of things. First of all, sometimes you simply need to set aside working code that is good enough and start developing new features, this is especially true in a start-up. Secondly, as new code and functionalities are introduced, old code needs to be maintained and kept up-to-date. It’s not an easy decision to refactor whole libraries of code to be easily adaptable to new unforeseen use cases, when you can write glue code to make it work instead. Furthermore there are almost always multiple developers working on the same code, and if you don’t have a common set of rules on how to program and how to introduce new techniques and conventions, the technical debt will soon start piling up. Very simply put: all tech is debt.
Being a new team, we felt it was natural to take a good look at how we worked and how we wanted to work going forward. We wanted to look at the code with fresh eyes to see how we could avoid tech debt to a greater extent and also learn in the process.
An essential part of being a programmer is to constantly strive to become a better programmer. We love to develop code without introducing bugs, to read and maintain code that seems to flow like a well told story, and to never worry about a change breaking a platform in production. Writing good code is obviously also good for your employer - not only in the sense that better code will make it easier to add the new features needed to increase revenue, but also that it’s easier to onboard new colleagues.
This is why we want to be code custodians. We want other programmers to look at our code and be delighted. We want our colleagues to be empowered to continue working on our old projects. And we want to leave a good legacy in the code base.
To make sure that we have consistent progress in our journey to become better programmers, we set a theme for each sprint. We pick a small topic such as naming conventions, how to format code or how to build small testable functions. Then we find some source of knowledge on this topic such as blog posts, coding conventions or other literature so that we can learn as a team and have a foundation to discuss. We go about our daily work in the sprint while thinking, discussing and improving our code base according to the theme.
While trying to keep the theme of the week in mind during development, we have also introduced a refactoring workshop each sprint. We use the ensemble programming technique as described below to apply what we learnt and to discuss questions that arise when actually implementing it.
For these sessions we book about two hours where we focus on refactoring and improving a small part of the code according to the theme, and about one hour to finish the code and put it in production.
We have noticed that it is important to have a narrow enough scope both in terms of code and concepts since you also need time to add/update unit tests and put the code in production.
If the code doesn’t end up in production, chances are that no one will take time to finish it and it will be dead code on some branch.
Having these workshops with the whole team not only educates us in the clean code craftsmanship, but also ensures that we all have a shared understanding of how it should be applied to improve the overall code base.
As mentioned previously, to ensure common understanding, we are using ensemble programming during workshops. Briefly described, ensemble programming (also known as mob programming) is when one person (the Driver) is writing the code and another group of people (the Navigators) are telling the Driver what to write. The idea is that this will spread knowledge within the team creating many people with T-competency.
To make the most of ensemble programming we have set up a few rules. They help us keep focus and make sure that everyone is heard and considered. After all, the idea is for everyone to learn and if the most experienced coder is calling all the shots, new ideas might be missed or less experienced programmers might not learn the why.
Our rules for ensemble programming:
Some benefits that we have seen from this is firstly that everyone gains a common understanding of what we consider good code, which enables us to be more vocal in our opinions. Secondly, it’s a great way to onboard new members, partly because you can teach all the ins and outs of your system, but largely because it’s a very good team building exercise. And finally it has made the code more maintainable as it’s easier to understand what it does.
Writing unit tests are an important part of the Clean Code standard, and there are many resources on how to do that well. We saw improvement in our tests’ quality as a direct consequence of our Clean Code workshops, even before we reached the test chapter in the book. We know that if we find it hard to formulate a unittest, we should probably go back and refactor the code itself. If we are not able to easily test a function, chances are that that function is not small enough and/or has side effects, both of which are things we want to avoid when writing clean code.
This is not a guide on how to achieve perfect code because there is no such code. This is a highly iterative process, where both the code and the developer get better over time.
If you want to try this out, we suggest that you:
Use ensemble programming to:
Remember to always make time to put the refactored code in production!
As we learn more about how to write clean code, all code is subject to change or even removal. All programmers want to be better - and we hope we have given you some ideas on how to start that journey! The first step can be as simple as abiding by the boy scout rule “Always leave the campground cleaner than you found it.”
Let us know what your experience is and happy coding!