Category Archives: Software Development

Not all PR nitpick comments are bad

This article is part of a series on reducing code review time. It helps if you have read these two first.

  1. If code reviews take too long, do this first (gather data)
  2. What to do about nitpick comments in PRs (eliminate them)

In the second article above, I define a nitpick comment as one that is just an opinion and not a defect, where a defect is something that doesn’t meet some objective criteria. A bug is an obvious example but so is a style-guide violation. A nitpick is a comment where the author and reviewer might disagree, but the issue isn’t objective, and there isn’t an obvious way to resolve the problem.

I recommend that you have almost none of these kinds of comments in a review, but there are a few cases where it’s ok.

  1. During onboarding: You should set the expectation to your new hires that the first month or so of PRs might have excessive commenting as a way of training on culture of the team. To make this less onerous, early PRs should be very small. The more junior the new hire, the longer this might last.
  2. If requested: Sometimes we are really do want to ask for opinions on code that isn’t ready for merge, but you want more eyes on it. In that case, authors should tag their PR as something that they would like more help on, and then any suggestions are fine. Authors that are doing new things in the codebase (and possibly setting new standards) should proactively request (nitpick) opinions.
  3. As a precursor to setting a standard: if you have a strong opinion that you think should become a team standard, then starting the conversation in a PR comment might be warranted. I would recommend that you quickly move it to whatever process that your team uses to change their standards (e.g. a retro). Use the PR as an example.
  4. If rare: If you think it won’t derail the PR and that the author would welcome it, then go ahead. A good example is pointing to an interesting technique that the author might not be aware of, but you think applies. This is more of an FYI than a request to change something.

What to do about excessive (good) PR comments

This post is part of a series on reducing the time it takes for a PR to go through code review. Here are the first two articles:

  1. If code reviews take too long, do this first
  2. What to do about nitpick comments in PRs

In step 1, we gathered data, and in step 2, we took steps to eliminate nitpick comments. But, we could still have slow code reviews because there is a lot of discussion about the pull request that needs to happen before the code is merged.

Some of this is fine. There will always be outlier pull requests that merit careful attention. However, if this is the norm, then it’s worth taking steps to reduce the code review feedback loop because (according to DevEx) long feedback loops are one of the drivers of low developer productivity.

When you have a lot of good commenting on a PR it might be a sign that the author isn’t checking their own code enough before they create the pull request. Spending an hour or so making sure that a pull request doesn’t have obvious problems saves the time of the reviewer but also reduces the feedback loop, which could go over several days.

On my team at Trello, almost all PRs were approved within a day because we had a culture of making sure that PRs were easy to review. I have documented some of our practices here:

  1. Construct PRs to Make Reviewing Easy
  2. PR Authors Have a lot of Control on PR Idle Time
  3. Pull Requests for One

To construct a PR that is easy to review, the code must be correct and also easy to know that it’s correct. Here are some ways to reduce the time it takes for reviewers to approve PRs:

  1. Use automatic linters and code formatters that make it impossible to PR code that doesn’t meet your coding standards. The reviewer doesn’t need to check for style problems as they are impossible.
  2. Go beyond simple automations. Automated review tools exist for accessibility, security, code coverage, static analysis, etc. These should establish a baseline to assist the reviewer and not replace them. Reviewers should not look at PRs until they pass the automated checking.
  3. If you can’t automate something, create a short checklist for the author to go through before posting the PR.
  4. If it’s clear that a PR has lots of problems, a lead should have a 1:1 with the author to train them on self-checking and pair program a better PR with them. This avoids back-and-forth that might take days. The goal should be to get the PR into a state that the lead would approve.

When PRs are slowed down because of excessive back-and-forth between the author and reviewer, but all of that discussion was necessary, that is an indication that not enough work is being done by the author to make the code obviously correct. Don’t discourage the commenting or short-cut the review in this case.

What to do about nitpick comments in PRs

In “If code reviews take too long, do this first“, I said that the first step was to gather some data on your code reviews to identify pull requests that take longer than your goal time. One common reason I have seen is that code reviews take too long because there are excessive small, unimportant suggestions.

Not all small and unimportant suggestions are bad. The problem happens when a it’s just an opinion and the code isn’t objectively wrong. So, for example, a typo in a code comment is worth pointing out because the author can fix it without further questions, and the comment is improved. Another example is a clear violation of the style guide, like using a variable with underscore separators when your standard is camelcase. In these cases there is no argument that the original code is correct, and it’s trivial to fix and recheck.

But comments that are just an opinion like: “I don’t like this variable name” or “you could use map() instead of iterating the array” or “this function is too long” are a matter of opinion in most cases. It’s not as clear what to do about it and might end up causing some more conversation. The author’s second try might also not satisfy the reviewer. If this code was clearly wrong, that would be fine, but it’s not worth the extended time to fix a difference in opinion.

To address the problem of excessive nitpick suggestions, the team should adopt a standard that a PR comment should be pointing out a defect. Here is a short list of some obvious defects:

  1. An off-by-one bug in a loop
  2. The screen doesn’t match the specification
  3. An error case is not checked
  4. Typos in variables, function names, or comments
  5. Using tabs when team standard says to use spaces (or vice versa)
  6. Putting business logic in a view when the team requires an MVVM pattern
  7. The code reimplements a function when it should use the one in our shared library

This is not an exhaustive list, but the pattern should be clear. A comment is pointing out a defect when it can compare the code to some agreed upon requirement or standard. It’s also clear what to do about it, and that it must be done.

So, if you find in your data that you have a lot of nitpick comments, gather them all together and go through each one and categorize them:

  1. OK: If the comment is pointing out a clear defect against an agreed upon requirement, then that’s a good comment, and the problem is not the comment.
  2. To document: If the comment is not a defect, but you think the comment is good, and there is a pattern of this kind of comment, then maybe this belongs in your coding standard guide such that it’s clear that the code is defective and what exactly to do about it. After this is documented, future comments of this nature are OK.
  3. To eliminate: If the comment is not a defect and you don’t even think it should be a standard, then this comment should never have been made.

After doing this step, in the future, most comments should be OK, and sometimes there will be a comment where it’s unclear whether it should become a new standard or not an acceptable comment. Things like that can be hashed out using whatever process you have.

It could still be the case that there are too many comments and that is still the main reason that code reviews are taking too long. In the next post, I will explain what to do about the problem when you have too many good comments on your code reviews.

If code reviews take too long, do this first

Short feedback loops are one of the drivers of productivity according to the DevEx model. On my team at Trello, we had a goal of all reviews being done inside 24 hours. Having that goal drove behaviors that made most reviews complete in a few hours. So, to start, collect data and get on the same page.

If your reviews are taking too long, try these enabling steps first:

  1. Gather metrics: If you use GitHub, try this repository metrics script to get a baseline.
  2. Get consensus: Nothing will happen unless the whole team is on board with this being a problem and that it can be fixed.
  3. Set a goal: I know from experience that 100% of reviews in less than 24 (work) hours is possible. If that seems out of reach, set something that you could accomplish in a quarter.
  4. Inspect outliers: Treat outliers like you would treat an outage incident.
  5. Compare reviews that met the goal to ones that didn’t: Gather statistics about PR’s and see if you can find differences between the ones that did and didn’t. For example: number of lines changed, the author, the reviewer, the number of commits, the part of the codebase, etc.
  6. Put real-time monitoring in place: If you are the lead, just do this manually to start. At the beginning of the day, make sure all of yesterday’s PRs are going to be reviewed soon.

Tomorrow, I’ll write about some common problems and what to do about them.

Four Ways to Augment Code Coverage

Code Coverage by itself is a hard metric to use because it can be gamed, and so it will suffer more from Goodhart’s Law, which is summarized as “When a measure becomes a target, it ceases to be a good measure.” Goodhart’s Law observes that if you put pressure on people to hit a target, they will, but maybe not in the way you wanted.

And this would happen with code coverage because we can always increase coverage with either useless tests, tests of trivial functions, or tests of less valuable code.

I use these metrics in combination with coverage to make it harder to game:

  • Code Complexity: The simplest way to do this is to count the branches in a function. I use extensions in my code editor to help bring complex code to my attention. If coverage of the function is also low, I know that I can make the code less risky to change if I test it (or refactor it).
  • Usage analytics: If you tag your user analytics with the folder that the code generating it is in, you can later build reports that you can tie back to your coverage reports. See Use Heatmaps for iOS Beta Test Coverage. In that post, I used it to direct manual testing, but it would work for code coverage as well.
  • Recency of the code: To make sure that my PRs have high coverage, I use diff_cover. This makes it more likely that my tests are finding bugs in code that is going to be QA’d soon and has already been deemed valuable to write. Very old code is more likely to be working fine, so adding tests to it might not be worth it. If you find a bug in old code worth fixing, it will generate a PR (and become recent code).
  • Mutations: I am still trying to find a good tool for this, but this lets you test the quality of your assertions in addition to your coverage. I do it manually now.

Generally, the way to make a metric harder to game is to combine it with a metric that would be worse if it was gamed in ways you can predict (or have seen).

Invaders game screenshot

Play Invaders on Glitch

My nephew and I are meeting once a week to make video games. We are using Phaser (and Javascript) as our game engine and Glitch as our coding IDE.

Here’s one of our game: Invaders. Since it’s on Glitch, you can see all of the code and “remix” it into another game. In the constructor of the Invaders class there are a lot of member variables that you can can to tweak the game to your taste.

I’m sharing this because I think that to learn programming, you should Start with a Working System, not use tutorials. This is more like what real programming jobs are anyway. Once you are comfortable with what the code does, then build a new game from scratch by copying over pieces a little at a time as you need them. That’s what I did to learn Phaser. The game I used is out of date, but I’ll share my fork and update soon.

In this game, we use

  • Sprites
  • Animations
  • Sounds
  • Keyboard controls
  • Collision detection
  • The physics engine: so that we can use velocity instead of updating the positions manually

You can make a lot of games with just those basic tools.

Apples and Oranges are (Relatively) Easy to Compare

I don’t like stack ranking because it’s hard to compare people into a rank ordering.

One of the most surprising things I learned in math was that complex numbers had no natural ordering. Meaning, less-than and greater-than are not defined for complex numbers. It makes sense when you think of it for a minute. The same applies to other multi-dimensional things like matrices and vectors.

So, why do we think we can rank order people? I’m specifically talking about companies that do this for their employees, but it comes up in other contexts (e.g. class rank).

People are hard to compare, but when we say that it’s like comparing apples and oranges, I disagree. Apples and oranges are both fruit, they both have around the same number of calories, they are about the same size, shape, and cost. They are easy to turn into snackable pieces (slices or segments). Even for me personally, I like them both about the same. On a lot of dimensions, they are about the same. When that’s true, the comparison might turn into just a single dimension where they vary more—maybe only in specific situations.

For me, the main way they are different is in how they travel. Eating an orange is more of a mess and harder to deal with outside of my kitchen. I’m much more likely to grab an apple to throw in my hiking bag or take to the beach. Another way is in recipes. I know a lot more apple desserts than orange ones. It stands up to baking better.

But, how about apples and raisins, or apples and candy, or apples and tempeh, or apples and bicycles. Those are harder to compare because they vary on more dimensions. In the bicycle case, they don’t even share dimensions except generic ones—they are both objects that have size and weight.

Getting back to stack ranking (which I still don’t like). Inside of a team it makes no sense to me. You would have a mix of levels and experience. That mix makes the team valuable, and arbitrarily favoring a dimension hurts the mix.

Like comparing apples and oranges (which is easy), it would work better if you could remove dimensions and only compare one or two. So, for example: compare just your backend senior developers with each other on just system design skill. You could reduce the set to just those with two years at this level. This might be useful when considering a promotion. In this situation, you might value mentoring and consensus building skills more than in-depth knowledge of TypeScript. So, it’s situational (like which fruit to use for a pie) and has reduced dimensions. Another advantage is that you don’t need a full ordering to complete the task.

Leet Code at Work

I prefer work simulation questions to leet code questions for tech interviews. I like to ask interviewees to write code that is similar to what we actually did was better than, for example, finding a successor node in a BST. At Trello, our tech interview would have you refactoring code in a way that is very common in iOS or implementing a UI from a spec. At Atalasoft, we had a lot of image processing algorithms in our code base, so I wanted to see you do something simple with pixels.

The other day I was thinking about my career and trying to remember if I ever did have to code a custom algorithm given a specification, and I did come up with a few examples. I’ve written before that my career happened to have a lot of math in it, and those same jobs sometimes also needed me to implement algorithms.

But more often, I chose algorithms (or knew that I needed to). I think that’s a more universally useful skill. It’s often the case that something just isn’t fast enough. A lot of published and common implementations of algorithms work well for the general case, but you may be able to make some assumptions in your specific application that allow you to do something better. Or your particular workloads might make a different set of trade-offs more appropriate.

To do this, it’s good to have broad knowledge of a bunch of choices, just so you know what techniques might be possible. These days, I think AI can help you a lot with this, but it helps to know what to ask for and when to ask for it.

Applying my first two programming lessons to teaching kids to program

I have been meeting with my 10 year old nephew about once a week to make video games together. It’s mostly just to have fun and for me to also chat with his dad, who helps on his side—we’re across the country from each other. His dad and I went to middle school together and got kicked out of wood shop and into a computer “shop” where we learned to program on the Commodore PET. We were 13 at the time and learned enough BASIC to make simple games. So, in a way, we’re carrying on that tradition.

My first program back then was to draw on the screen given a list of coordinates. It was about 5 lines of real code (and a dozen lines of DATA). It was easy to understand and it put stuff on the screen right away, which was my first lesson in programming.

It’s hard to do that in modern programming, especially if you want to be able to keep going on to build something more real. There’s just so much boilerplate and ceremony to just get something on the screen.

After some trial and error, I have settled on using Phaser as our game engine and Glitch as our editor.

Glitch lets you edit code like a google doc. We can both be in the editor and change code simultaneously. Ironically, it took me time to remember Glitch even though I got to witness its genesis inside of Fogcreek when I was working at Trello.

Glitch is easiest to use with JavaScript, so I had to pick a JS game engine. Since someone already had made a remixable Phaser game, I started with that. My second lesson in programming is that it’s easier to learn how to modify a working thing than to make something from scratch. This is really an extension of the first lesson—when you are making something bigger, it takes too long to get something working, so to learn how to make big things, start with the big thing already done. First you modify parts to get a sense of all the pieces, and then later, you will be able to make one from scratch.

I’ll post some of our games next week. They are mostly simple ports of classic 2D games from the 80’s and 90’s.

Mutation Testing

A couple of years ago, I wrote about a testing technique that I had learned, but didn’t remember the name of, so I called it code perturbance. I mentioned this technique in my book, and a helpful beta reader let me know the real name: mutation testing.

The idea is to intentionally change the code in a way that introduces a bug, but is still syntactically correct (so you can run it). Then, you run your test suite to see if it can find the problem. This augments code coverage, which will only let you know that code was run in a test, not if an assertion was made against it.

Now that I know the name, I can find out more about it in google. For example, there are tools that can do it for you automatically. The one that I’m most interested in is Stryker-mutator, because it supports TypeScript. I’ll report back when I try it.