How we develop

Thiago Garcia da Silva
metr tech
Published in
7 min readAug 5, 2021

We are a fast-paced company, and by that, I mean that we can deliver a good amount of value for our clients fast enough. What I don’t mean is that we rush into things, or we overwork. It’s quite the contrary, we take the time needed to do the right thing. The catch here is that we value agility over velocity, and that’s what leverages us to do what we do, and a big part of it is rooted in how we code. Our code must allow us to be agile, so it must be easy to change, and it must be easy to change for anyone in the team, not only for the ones who did it. We do a few things to help with that.

First, no code is always better than any code. Because we are focused on solving a problem, it’s not abnormal for us to decide to not write code and solve the problem in some other way. Coding is a tool to solve problems and like any other tool we shouldn’t use the same one for all the problems we encounter. We frequently prefer to spend time understanding the problem to solve the root of it with the least amount of code possible. Extending libraries to meet our needs instead of changing everything is a big part of that. We gave a talk in DjangoCon Europe 2021 about that.

In the talk, we show how we use an Excel renderer in one of our views. Exporting data in different formats is no big deal, but the point here were the discussions that led us into doing that. One feature that we have is a list of alerts for devices and that’s pretty interesting to investigate, so the ability to analyze the data is something that our clients wanted to have. We could have just started to add filters for every column, ordering, comparisons and so on, we are a good team, none of those tasks are a challenge for us. But that was just one of the ways to solve the problem, and we found out that not even the best way to do that. Giving an Excel file to our clients was a much better solution. Let’s be realistic, we stand no chance in competing in features with Excel, so why not let them use it? And that was our solution. The problem of data analyzing was solved, and it involved just adding a new renderer.

To be able to arrive at those decisions, we rely on many well-written problem descriptions that become the documentation for that feature in the future. We have a post about how remote work had a massive impact in that. Our issues on GitHub aren’t born as very technical issues, but a good problem description that we will refine together, still focusing on the problem, not in the solution. We want to validate if we all understand the problem and if it is really the root of it, not just a symptom of something bigger. Only then can we start to brainstorm a possible solution together. Later, the team working on that issue will have closer discussion with the PO. And while doing the technical work they can even discuss new solutions, given that, many times, your assumptions changes after working for real on the problem.

It keeps us centered on the problem we are solving, and having this super short feedback cycles with the PO is the key to avoid working in the wrong thing. The Excel example that I mentioned before is a good one to understand that. By having a clearly defined problem, and not a predefined solution, we avoid working too much in the wrong feature and open space to more creative solutions. All of this is later there in the GitHub issue to be found for who needs to understand why that was the decision at the time, all linked by good commits and PRs.

To help us keep the focus on what matters, we do pair programming. Most of the time, we are working with someone else. Having someone with you while coding can be a different experience and not always the most pleasant one, you need to share more and go through the pain of solving a problem together, which is not always easy. But because our whole process is based on that, we created enough space to accommodate the extra time needed: while you are still adjusting to your pair, or the time to share your knowledge, or even the time talking off topics because you are both too tired of coding.

Pair programming is an essential part of our code culture, and the benefits of it outgrow the extra time it consumes. It allows us to have way faster iterations and exchanges that a PR, for example, could ever allow. So crucial decisions can be made with help, with a second pair of eyes to make sure we are going with the right solution and if not, we can change right away. If you see a PR and have a better solution for a problem because maybe you already solved that problem in multiple different ways, it’s super hard to ask to change the whole thing. This isn’t fair to the person who created it. But if you are doing it together, it’s easy to adjust as you go.

Another crucial part of how we develop is TDD. The tests really drive our development. Even tough all of our applications have hundreds of tests and our test coverage is excellent, it’s just a consequence of TDD. Tests for us are not just that thing that runs before the deployment in the CI and takes tens of minutes to run to try to avoid breaking something. We write tests before coding the solution, it’s the tool that helps us understand what we are doing, which problem we are solving, and it will guide us in the best direction. It’s powerful to think on your functions and classes APIs before coding them. But TDD requires a lot of practice, and to help with it, we do a coding dojo to practice and elevate our skills at least once a month.

It’s not rare to read someone saying that TDD tends to produce a very characteristic code, and I agree with that. Most of the time, it will kind of force you into separating concerns better and avoid coupling. And you see the real benefits of it when you or a teammate needs to change something, and it’s obvious where and what needed to be changed. We have so many commits that are just a new test and a new line in a specific place. In the end, it’s the coding tool that help us keep agile and avoid us creating legacy code.

Finally, there is something that many times is overlooked by developers: Git commits. We have other posts here talking how commits are a central part of our development. I will give you a real example of how commits can look like at metr:

Adds udev rule to create a symlink for each usb device of the quectel moduleThe quectel module creates 4 usb devices[1], the third one (despite
the documentation saying it's the second) is the serial port to which
we can send commands and communicate with the device.
Usually, on boot the modem is the only USB device connected, so this
is /dev/ttyUSB2. If there is another USB device connected, though,
this can change.
The udev rule created here, uses the interface number provided by the
module itself on each device to create a symlink with a fixed number,
so /dev/ttyCellular2 will always point to the serial port that
receives AT commands.
The rule can only match on the device itself (in this case the
`/dev/ttyUSB?` ) and one parent device. this means we cannot use a
single rule to match each port, because the `idVendor` and `idProduct`
attributes are in one parent device and the `bInterfaceNumber` attribute
is in a second parent device.
To solve that, we have a first rule that matches ANY of the 4 devices
from the modem and creates for them an environment variable with the
`idVendor` and `idProduct`. On another rule, we can match on that
variable and the `bInterfaceNumber` that tells us which of the 4 ports
we're handling right now.
Closes metr-systems/backlog#1234[1]: https://sixfab.com/wp-content/uploads/2020/12/Quectel_LTE5G_Linux_USB_Driver_User_Guide_V2.0.pdf

This is one of the more extreme cases of commits, we also have commits that are just good titles. But we do have many commits with descriptions, especially if the change is something that can cause confusion in the future, and that ranges from the one I’ve pasted here to small paragraphs. Coding for us is, in a way, a form of communication and especially with Python, you can code in an extremely expressive way, so why stop there and not bring this to the rest of our communications?

Of course there isn’t a silver bullet, and it’s not always that we can follow all of that, and for that, we have a last card in our sleeve: adapting. We have periodical meetings where we discuss how we can get better, how we can adapt. What was working fine for us when we were just 2 people wasn’t working anymore when we were 6. An example is how much more documentation we need now, it isn’t enough just to keep information between those who’ve done a job, but it needs to be accessible to everyone in the team. We talk more about it in this blog post about remote work. We needed to adapt, review our assumptions and create new solutions to keep the goal of being agile and keep delivering value without having to overwork ourselves.

--

--