Disagreements about code style, and how to solve them

object of type 'social contract' is not subsettable

If programming is hard, programming as a group is np-hard; especially when the group of people working on a programming project is mostly scientists, who are used to working alone, and have each developed their particular sweet coding style over years of unguided and unstructured practice. After posting some unstructured ideas on the VERENA groupchat, I thought I would aggregate my take on “how to disagree on code” here.

It is fundamental to separate two things. Bad practices, and different tastes. Following good practices should be a given. Comment the code. Test. Sanitize inputs. Test. Check outputs. Test. Minimize surprises. Test. Document. Test. Version control. Test. Specify dependencies. Test. You know the drill.

Aside: I am trying to make a point about testing because as an editor, I have returned a substantial number of software notes to authors asking them to resubmit when they have added tests, and subsequently read many resubmissions that said “we have added the tests and identified several new bugs, which we fixed”. Your code is bad, my code is bad, everyone’s code is bad, just test.

But having different tastes in how to write code is something much more challenging to solve. One relatively low-effort way to address the least challenging part is to pick a style guide, and stick with it. For example, I (and by extension the lab, and we’ll get to that in a minute) use BlueStyle, except when I don’t. The point of a style guide is not to make the right decision. There is no right decision about style. The point, instead, is to make the same decision every time and remove the need to guess.

But “style” also covers higher-level decisions than whether you should use camel case for variables (you shouldn’t) or add spaces after a comma when using multiple arguments in a function (you should). Style is also about “building the interface” of the code, and this is usually when all hell breaks loose.

Let me give an example - these three lines of code do (or, like, they would if we had actually written the code) the same thing:

train(RandomForest, labels, features)
RF.train(feats, labs)

It’s because code is fake. It’s just how we name things, and we can name things an infinite number of ways and have the same output. The last line (k(i,j)) is obviously bad because it conveys no information about what it does. It has no semantics. Would I write a line exactly like that? Yes, absolutely. Nested deep into a function that is not user-facing, where the context makes it clear what k and i and j are. I wouldn’t be proud of it, but it’s not something I wouldn’t ever do.

Picking one of the first two lines is likely to be difficult. Let’s examine them in detail. The first line has a function called train, to which we specify what should be trained (a RandomForest), and with what (labels, which derive from features). The second line has a function called RF.train, which trains a random forest, and it works by looking at feats to predict labs. Both are logically consistent, convey some semantics about what is being done, and can be justified.

And yet, I would sooner pour bleach into my eyeballs than write the second one. Not because it’s wrong, but because it doesn’t feel right. So assuming we are having this situation in real life, who gets to pick which style we use?

The pre-answer to that is, yes, we absolutely need to pick a style, because a consistent style that some collaborators disagree with is always better than a mix and match of multiple styles. This is why good style guides start with a disclaimer that you should put consistency over adhering to the guide. Not choosing, and letting every person do its thing, is not sustainable.

Now let get back to the part where I mentioned that my use of BlueStyle extended to the lab. I am working from the assumption that I will need to onboard people to the packages for development or use, that I will have to look at the code from multiple packages, etc, and that this is also true of lab members – it makes sense that there is a “way” of doing things. It might not be the best one, but it is better than not having a way at all. And over time, this way evolves into a sort of culture, where we collectively use the same design patterns.

Whose patterns we use depends on who is going to be maintaining the package. I recently submitted a new feature to a package, where I made a design decision; the maintainer disagreed; we went back and forth outlining our reasons; I yielded to the fact that I was not going to lead the maintenance of this package. Do I like the decision? No. Will I respect it? Absolutely.

And this is the core issue: “the right style” is a social contract between multiple people. And there are often equally valid arguments to make completely different decisions. The question that matters is then, “who will bear the burden of maintaining this code”? And this is the person with the final say. Disagreeing about code style is not a technical exercise; it is a communication one. It is an exercise of figuring out the role of the different people in the team, and the duration or intensity of their involvement. Having general guidelines within a project, and a commitment to abide by them as long as it makes sense, is a good practice, but when the number and diversity of collaborators increases, respecting the leadership of the people who are leading the project is probably more important and more sustainable.