Paul BecotteAdmin

Using Values for Software Decisions

Its important to be clear on what you value- it helps you make decisions, and align teams to do work more easily

Recently, while browsing Reddit, I came across a comment that stuck with me a bit.

"In a relationship, shared values are far more important than shared interests."

The reason it resonated is because it describes my marriage pretty well. Outside of some overlap in movie taste and liking most of the same foods, me and my wife share very little in interests. In fact, mocking her for her Hallmark movies or her mocking my love of Musicals is one of our things. On the other hand, we have very similar values. The importance of family, putting each other before ourselves, the way people should interact with one another- we agree strongly on these things. It seems to work pretty well, coming up on 19 years.

Tangentially, we were having a work debate about the right way to setup some tool, and I was trying to understand my preference for one approach versus the other. In formulating my argument, I came back to that Reddit post, and I think it explains it pretty well. When making decisions, I have certain things I value. This is on a continuum, and like the Agile Manifesto, its not saying that the other side of each coin has no value, just that I value these things more. I decided on the following three as most important to me-

A development workflow should be a single command.

I would argue that the second you're writing out directions on how to do some task that includes more than one step, you're probably setting yourself up for failure. "make test" and "make deploy" are much easier to get right than "run these 7 commands". I worked with an ansible playbook recently that had a long wiki document explaining the different orders that you should be applying the various tags in. Nobody was confident enough in that thing to use it for any real work. The counterpart to this is that "explicit is better than implicit." Its true, hiding how things work so that users are not even aware that step 4 involved connecting to some remote machine and updating a data file can lead to hard debugging down the line, but for processes that are meant to be done often, I think you have to handle that with documentation and learning while relying on the automation for the intended integration point.

A development workflow should depend on as little external setup as possible

If the script depends on a particular python virtualenv being activated with the right packages, or on the version of the gcc toolchain installed on the host, or particular functions in the users .bashrc, its a recipe for pain. The best CI/CD setup I have been involved with had agents where the only software allowed to be installed was Make, Docker, and Docker Compose, and the only command that Jenkins would run was 'Make ...". It meant that developers could always replicate the CI environment, and it forced people to have hermetic workflows. Over the last three years, the number of hours I have spent with some engineer with "this worked yesterday and now doesn't" is kind of mind boggling. First you have to understand what they're doing, and then you have to understand HOW it worked- before figuring out what changed so that it doesn't anymore. If you're going to have a one command workflow, its important to understand the assumptions that workflow makes about the environment. I have a lot of reservations about bazel, but this is the reason I still think it can be valuable- its intended use case included downloading/installing whatever is required for each step to work. The counterpart to this is something like direnv. If it is installed correctly, it can let you enable some convenience as you change into various projects and run commands. I would argue, however, that it takes the wrong approach, by requiring an external setup step. Instead, have the command in each repo be responsible for setting up its environment.

It should be easy to follow the logic of development workflows

This builds off of the first two and offsets them a bit, but is most important. If you're going to write a script that does a number of steps, it should be straightforward for someone debugging to understand what those steps do. The worst is when a script calls another script that sends a request to some service that sets up something else, with no comments or output. This is, in fact, one of the worst things with Bazel, where I encounter rules all the time doing unexpected magic side effects, but it happens everywhere. I don't believe that explicitness is worth expecting users to type multiple commands or install their own dependencies, but in the code and scripts themselves, it is important to be as explicit as possible.

So, hopefully these values make sense, at least as a framework for me to think about tradeoffs and decisions when setting things up. I also have a theory that hiring for shared values, much like my marriage, may be more effective than hiring for skills and knowledge. We do a really bad job of understanding skills (I have gotten feedback from interviews that I am not technical enough- I have also gotten feedback from two separate managers that I am the best engineer they had worked with) - but values should be easier to get a good signal from. Further, the amount of lost work on teams because they don't agree on fundamental principals and spend time rewriting each other's code is non zero. Often there are multiple valid approaches to a problem. Having a team that at least agrees on what a "good" solution would look like makes it a lot simpler to reliably make those decisions.