Question on r/ExperiencedDevs: Getting Code Reviewed Faster

I saw this question on the ExperiencedDevs subreddit today: My colleague’s code gets reviewed no questions asked. Getting mine reviewed takes a couple of nudges. How can I improve the situation? The answers on this subreddit are usually helpful, and I saw one that I resonated with me:

Personally quick PRs, sub 15 minutes total review time, I consider to be a mini-break from whatever feature I’m working on over the span of multiple days. If your PRs are 1000s of lines long and require an entirely different headspace it may cause delays that way.

This is what I was getting at in PR Authors Have a lot of Control on PR Idle Time where I told a story about the analysis a colleague at Atlassian did on our PR data. He found that short PR’s had less idle time.

On my team, where short PRs were highly encouraged, most reviews were done in a couple of hours, with 24 hours being the absolute max. Any longer than that would often be resolved by a live 1:1 review because it meant that the code was too complex to be reviewed asynchronously.

The thread on r/ExperiencedDevs has some more advice on solutions that try to resolve the social aspects, which are helpful, but I do think PRs that are easier to review will sit for less time. If your code is inherently complex, there is still a lot you can do to make the review easy (see A Good Pull Request Convinces You That it is Correct).

Applying Program Language Learning Techniques to Learn a Foreign Language

I’m an “expert” in learning programming languages. Just counting languages that I have worked with professionally for at least 5 years, I know more than a dozen and I am 4 years into adding TypeScript to that list. But, I only speak and read one non-programming language, English, proficiently.

Learning traditional languages has not been easy for me. I learned French in school just enough to pass my statewide tests and didn’t retain enough for practical use. I tried DuoLingo for Spanish a few years ago, but felt like I got caught in a rut of naming farm animals.

But now I am planning on going to Germany for vacation this year, and I would like to know more than I do now. I know a little tourist-level German from having to travel there for work regularly in the early 2000’s. Not enough for even a simple interaction, though

Since nothing I have done has ever worked, I want to do something different this time—perhaps building on my programming language learning experience. The easy thing to see is that put serious time and energy behind learning a traditional language. I don’t have that problem with learning programming languages. I usually spend several hours a day using them when I want to learn them. Also, I have external motivation: when I got a .NET job with 0 .NET experience, I needed to learn C# fast, but I was paid to do it.

My motivation will be to have fun experiences when I am in Germany. Most of the time I will be able to use my phone to translate written text (e.g. a menu). If I need to know something quickly, it will likely be because something is being spoken to me. I might want to interact a little better with people in hotels, restaurants, and other tourist attractions (where taking out my phone would be awkward). This means more of a focus on listening exercises.

Finally, when I learn a programming language, I usually start to make something practical early into it. I can do this because I can program already, but for novices, I recommend starting with whatever canonical language book was written by the designer and to generate focused exercises that use only what you know. I’m a novice, so that’s the approach that I think I should take.

So, here’s the skeleton of my plan

  1. Allocate serious time to it.
  2. Convert some of my random YouTube and podcast consumption to German audio content.
  3. Find or generate exercises beyond DuoLingo so that I can practice remembering more vocabulary.

A Good Pull Request Convinces You That it is Correct

My onboarding peer-mentor at Trello described a good pull request as telling a story. In practice this meant that you would edit and order the commits after you were done so that the reviewer could go commit-by-commit and understand the change you made in steps.

So, for example, let’s say you were working on a feature. In your second commit, you introduce a bug, but then in your fifth commit, you find and fix that bug. Instead of just PR’ing that, you would edit the commits so that the bug was never introduced.

This is analogous to sending a document with superfluous text deleted, not crossed out. If you don’t edit the commits, you will waste the reviewers time because they might see the error in the second commit, make a comment, and then have to go back and amend their comment in the fifth. If you did this a lot, they might not even finish reviews before rejecting them (which is what I suggest you do to PRs with obvious problems).

I like the story frame, but I have started to think of a PR as more of an argument of its own correctness. I am trying to teach the reviewer the details of the problem and convince them through evidence that the new code is correct.

In a PR, I might start by introducing a unit test into the area you intend to change. Then, to make things clearer, I might commit a small refactoring (that isolates the change). It’s now possible to add more tests and possibly a failing one that shows what my intended fix will address. My small code clean-up commits are in service of that argument. Without them, it’s hard to tell if my fix won’t break something else. With them, the fix feels like a natural and inevitable conclusion.

Like a philosophical argument, I will anticipate and address the cases the reviewer might think of. But it’s not enough to handle a case in the code, your whole PR needs to make it clear that you anticipated and addressed it (with tests, comments, screenshots or any other evidence you can think of).

But the most important reviewer to convince is myself, of course, and doing the work to write the argument gives me confidence that my code is correct.

One Time I Recognized a Junior Catalyst

This is a followup to Some Behaviors of a Catalyst where I said that I didn’t think you needed seniority to be one.

More than 10 years ago, I was interviewing college seniors for an entry-level position. Among the group of very talented students there was one obvious front-runner who we offered the position to (and who accepted). But there had been another candidate that impressed us.

They interviewed well and were qualified for our position. But, we preferred candidates with some C/C++, and they had less than our preferred candidate, maybe none, I don’t remember. In any case, that was probably the deciding factor. But in a lot of other ways, they were outstanding (in the literal sense of the word). They stood out.

Here’s what I mean. A bunch of the candidates had worked together on their senior, two-semester, capstone project. We heard about this project from several angles. Over and over, a candidate would point to another person as the reason the project went well. They would, of course, talk about their own contributions, but they would also specifically point out what this other person had done as well. The thing was, the other person was always the same person, the “outstanding” one.

Even with this clue, we didn’t hire them.

About a week after we had made our decision and the offer, my boss called me in. He said a friend of his (another tech business owner) had sent in an unsolicited recommendation for a student he knew. I think he had them as intern or something. In any case, the recommendation was effusive. The student was, of course, that same one who had been recognized by their peers.

At this point we felt as if we’d be idiots not to hire them, but we didn’t have a position. The good thing about junior developers, though, is that they are cheap and you should always have a position if you see a good one. So, we created a position and made an offer. I think this is the only time I recognized a junior catalyst before working with them—the key is that people like working with them and attribute success to them. When that fact comes to light without seeking it, it’s a good sign.

Some Behaviors of a Catalyst

Yesterday I wrote that you should recognize catalysts, but didn’t say how. The problem is that so much of what they do is under the radar, but one tell is that they always seem to be part of successful teams and everyone on the team knows that they were an important member.

Catalysts can be technically great, but perhaps not the most technical person on the team or the best specialist on your corner of the company. In my experience, they are more likely generalists. They likely know the most about the other teams in your organization, which is why they can get things done with them. They often know who to go to, and those people often know them.

It’s not just the other teams, they also know the most about the architecture of other services. They might even have commits in repos they don’t own and in the third-party dependencies they use. When they need to get things done, they don’t limit themselves to just their own codebases.

Their colleagues like them and like to work with them. And the catalyst genuinely liked working with those people too (and they like people in general). They are often your best source of candidates. The best way to hire a catalyst is through a referral from someone on your team that raves about working with them.

Knowing what I know now, if I were to look for them I’d be looking for what Atlassian called “Feature Leads”, which were engineers that led a “feature” project. They often needed to orchestrate the work of several other people and perhaps several other teams. I guess to make sure they were good at it, I’d be looking for people that did it repeatedly at the same place. This work doesn’t rely heavily on their commits, but I’d be interested in people that had commits in disparate parts of the feature, not because of the technical skill it requires to do that, but because it would indicate a holistic view and might indicate a tendency to clear roadblocks, both of which are catalyst behaviors.

As I said in How Senior Software Developers Think, the more senior you are, the more you think of how your work relates to the mission of the company. Catalysts at any level do that. It’s often easier to have success as a catalyst the more senior you are, but I don’t see this as a requirement. Feature leads at Atlassian could be just past the junior level (pre-Staff/Senior level). It was a good indication that they should be promoted, but they didn’t need to have that level to catalyze.

Recognize the Catalysts

I tried to pattern my career after An Unnamed Programmer in Peopleware, whose contributions weren’t always clear, but “had never worked on a project that had been anything other than a huge success.” She was overlooked by her management, so I learned that to do this, you needed to make your contributions clear.

As a manager, I didn’t always know how to hire for this quality, but I knew it when I saw it and made sure they were on the projects that mattered. And I keep track of them, too. I might not know if a new candidate is one, but once you know someone is a catalyst, you can always hire them at your next gig.

A Little More at the End

I recently added 10 minutes to the end of most of my workouts. This lets me get the effect of another workout without having the overhead. In 10 minutes, you can burn close to 100 calories, and in a week, that would be 500. I can’t think of an easier way to do that, but that’s only because I already have these workouts on my schedule.

The same thing could be applied to other things, but probably it’s not as quantifiable. I could code for 10 more minutes after I’m done coding, but when I’m done, I’m usually spent. The same goes for writing. This makes me think my workouts aren’t taxing enough. Maybe the 10 extra minutes will change that.

Progress Bars, not Time Blocks

In Deep Work [affiliate link] (and his other works), Cal Newport has been a proponent of time blocking. He even sells a planner based on his method, which is probably the best place to learn about it. In short, he gives each hour of his day a specific task. In the happy case, this works well, but it breaks down when you get interrupted, get blocked, want to extend your time, or lose energy. He does address all of that, but that’s where I have found his method to break down for me.

Instead, I’ve been doing something my friend Joel Mazza taught me, which is based on the idea of a guiderail made up of 30 minute time blocks. Like Newport’s time blocks, there are things in your day that need to happen at a specific time, usually meetings, so those go on the guiderail in the appropriate place. After that, the system diverges in that we will build up our daily tasks and assign them an amount of time, but not a specific time on the guiderail. We just need to make sure we have a reasonable amount of work that fit in our day (and make sure to have some buffer).

Instead of letting the clock decide what we will do each half hour, we can pick based on our energy (or at least, that’s what I do). I had a half-hour before lunch, and so I am writing this post. When lunch is done, I have a lot of meetings in the afternoon, so I am doing it now, so I can be free after. Earlier today, I tackled some book editing, and I have a 12:30 accountability meeting to get back to it, so that’s why I felt free to switch.

There’s a lot more to it, but the key ideas are progress and flexibility.

I Prompt Cursor Based on My Progress Bar

In Build a Progress Bar for Your Work I explained how I take a task and break it down into subtasks. Each subtask then becomes a commit. A benefit I didn’t mention is that I can look at the commits and see the time it took to do each task, which will be a good way to adjust my estimate going forward. Unlike in Using Zeno’s Paradox For Progress Bars where you have no clue how much time is left, I actually do have some idea.

Having this progress bar of subtasks has also been useful in working with Cursor. I use each of those subtasks to drive my prompt. Here’s an example:

I am working on an app that has a thing like a feed with a concept of posts and reactions, like slack or discord. My stack on the server is MySQL, a TypeScript based ORM, and a TypeScript based GQL wrapper that is served by node and Apollo. My Tasks/Subtask progress bar looks like this

  1. Add Reactions to Backend
    • Create reaction entity and relate to shared objects
    • Create SQL migration
    • Create ORM service function to react to a shared object
    • Test the service function
    • Update shared object ORM getter to have a reaction summary
    • Test the reaction summary
    • Add a field resolver to the shared object resolver to get the reaction summary

Having that, I use each sub-bullet to create my prompt. I give Cursor a lot more information though. As an example, here’s my prompt for the first bullet (I also need to put in the context files)

Add a reaction.entity.ts with a UUID id, a many to one relationship with sharedObject (and do the reverse in sharedObject). Have a reaction type (string), a User, a createdDate (like sharedObject) — do a unique that is like this UNIQUE (shared_object_id, user_id, reaction_type), but in TypeOrm syntax at the top of the class

From that it knows what fields I would likely want to index, and adds that too. It picked the delete rule I usually use (CASCADE), and it updates the User entity and SharedObject entity to have the reverse relations. It made some slight errors, but they are easy to check and fix.

Once I have that, I make a commit and move on to the next one. In that case, I use an external script for migration, so I just do that myself, and commit.

I go on like that, picking the next thing in my list, coding or generating and fixing, and then making a commit. The result is a PR that looks the way I want (not just the whole change in one commit) in the order that makes sense for a reviewer. I do this even though I am the reviewer (as I wrote in Pull Requests for One) because I do actually do a review and I want it to be easy.

This activity (to me) is very much like programming, which is what I was getting at in Can non-programmers use Cursor? A complete non-programmer? Probably not, but I do think someone could do this if they had some programming skill. The main thing I am doing is what I said in Programming is Disambiguating: “Programming is taking a nebulous problem and breaking it down, understanding it, trying to find building blocks, and then building up something that solves the problem.” Those building blocks could be prompts or code, it doesn’t matter, as long as it solves the problem.

Any Thing Can Be a Blog Post

I’m catching up on this blog (I Write for Yesterday when I skip a day), because it’s been hard for me to think of topics. For me, it’s February 7, and I just wrote the February 4 post about the Fantastic Four teaser’s portrayal of AI a few minutes ago. I actually had the idea for this post first, which was going to incorporate yesterday’s, but I broke that out to stand-alone.

This is all to say, I think it’s always (or very often) possible to turn anything you think of into a blog post, or any Thing-based media, if (like me) you spent a lot of time watching YouTube recaps of the FF teaser. I wasted a lot of time on February 4th doing that, but I believe in Monetizing Waste, and even though this blog doesn’t make money directly, I got two posts out of it, so that’s enough.