A lot of the initial responses we get about Review Board go something like this:
Review Board looks cool, but it’s designed for pre-commit reviews. I don’t like the idea of needing review before committing, for a number of reasons. Can my use case be supported?
The short answer to this question is “Yes, it’s supported, but our UI isn’t optimized for that case.” In particular, it requires using the post-review command line tool to create the review request instead of having a nice web UI for selecting a revision to review, and it’s almost completely undocumented.
While we’re interested in generalizing Review Board into a tool which supports a variety of workflows, I’m not writing this today to explain how to make it work. I’m writing this to explain why all of you who think this are wrong.
Before I finish my attempts to offend everyone, some people should leave the room:
If you’re working in an industry such as telecom or aerospace, and you have a very intensive code review process to ensure perfection, this probably doesn’t apply. I appreciate your hard work ensuring that the airplane I’m flying in won’t fall out of the sky, and that I can call 9–1-1 when needed.
Next, if you’re not using version control, stand up, hang your head in shame, and go set up a server. SVN, Git, Mercurial, it doesn’t really matter. Visual SourceSafe is maybe a bad choice, though. Seriously, version control is number-freaking-one on the famous Joel Test. Once you’re up and running with that (get a bug tracker too, while you’re at it) you can start thinking about code review.
Okay, now we’re down to just those enlightened, version-control-using hackers (ahem, software engineers) working on the sort of normal software that we do. I promised to offend the rest of you. Here we go.
It’s often kind of hard to define code review, since so many people do so many different things. It covers the spectrum from “occasionally glancing at a diff when a commit message sounds dubious” to those autopilot engineers I already told to leave. In the absence of any sort of authoritative definition, I’ll go with Wikipedia’s:
Code review is systematic examination (often as peer review) of computer source code intended to find and fix mistakes overlooked in the initial development phase, improving both the overall quality of software and the developers’ skills.
Why should I care?
That’s probably as good a definition as any, and I almost fell asleep before I finished reading it. Yawn. What the heck is this good for, anyways?
The big, super important reason anyone does code review is finding mistakes. We all make mistakes, sometimes. Even those superhuman hackers who seem to write 10x as much code as everyone else where every line looks perfect. It’s really easy to stop really looking at code after having read through it a few times. Ever spent 10 minutes trying to get something to compile before noticing that you missed a semicolon? A child could look at that code and tell you what’s wrong in seconds. It’s not that you’re dumb (well, maybe), it’s probably that you stopped reading and started remembering.
I remember a paper I wrote in college. I think it was about medieval Japan. My mother offered to read through it for me, and I wasn’t about to refuse some free editing, so I sent it to her. In one sentence, I had two “the“s. That’s right. It said something like “In the the Heian court…”. I must have read that paragraph twenty times before I sent it, and I had just stopped paying attention to what was on the page. I said that even superhuman hackers make mistakes, and that means even me.
Finding mistakes is pretty obviously a good thing, I think. After several decades of using software, people have pretty low expectations for quality. You wouldn’t believe how often I tell people where I work and they start talking my ear off about how our software never crashes, and how it’s such a different experience from, well, almost everything else they have to use. It turns out that writing software is really hard. Shocking, I know.
The next big reason for code review is training. Most large software projects have some underlying architecture and design principles which can take some time to learn. One example that immediately springs to mind is the way we use signals and slots in our UI code at VMware; this sort of asynchronous design can take a while for people to get the hang of, and providing suggestions during code review is a great way to get people up to speed in the context of doing real work.
Code review also helps us improve our truck factor. This is my term for “if you were to get hit by a truck tomorrow, how many people would understand the code you’ve written?” If it’s been peer reviewed, that number will be at least one. Gas prices may be driving down SUV sales, but there’s still a lot of trucks. Be careful out there on the roads, guys.
And last but not least, it’s so we can learn from each other. I’ve lost track of the number of times I’ve seen something I didn’t understand in someone’s code and ended up learning a new trick. Everyone likes new tricks, right?
How to ruin everything
So, overall, code review seems like a pretty good thing. We can improve the quality of our software, train new employees/contributors, and learn stuff all at the same time. Now lets look at how people do it wrong.
The first extreme I’ve mentioned is where everyone gets together and pores over some code to find every problem with it. If you’re doing this and your software doesn’t have lives on the line, stop it. Perfect code is a nice ideal, but “sometimes it is not worth fixing a bug.” There’s no way that an entire codebase can be scrutinized in this detail and still live in the fast-paced world of software.
On the other extreme, the easiest way to do it wrong is just not to do it. Sure, you may get an extra few hours a week to surf pornography and make faces at the dude in the adjacent cubicle, but it’s not gonna help the quality of your software.
So, assuming that you’re somewhere in the middle, and you’re doing code reviews without interrupting your developers’ lives too much with hours of meetings, you’ve probably fallen into one of two camps: pre-commit and post-commit. There are some pros and cons for each, but to me, the choice is obvious. Here’s why.
In a post-commit environment, a developer makes some change to the code and submits it to version control. At this point, usually in an automated fashion, that change is sent out to the other developers. This almost always takes one of three forms:
- The commit message and a list of files is sent to a mailing list (lame).
- The commit message and a diff is sent to a mailing list (slightly less lame).
- The change is automatically made available to a tool like Review Board or Trac (okay, I guess)
At this point, those other developers can look at the code and see if there’s anything they disagree with. The big, major, 600-pound hairy smelly gorilla of a problem with this is that /they don’t/. For example, one of the people who checked out Review Board and asked about post-commit reviews wrote this:
Sure, some bugs can (and do) creep in because they are missed in larger commits where we don’t read the entire email, but I’m not worried because QA should catch those…
It’s exactly the larger commits that need the most review! Any developer writing a three line trivial bugfix patch can convince themselves fairly easily that it’s not going to completely fuck shit up (that’s a technical term). It’s a bit harder when the patch is thousands of lines and implementing complex functionality.
Also note that unless you’re working on a private branch (which is fairly rare), committing that big patch before review has a good chance of breaking stuff. Raise your hand if you’ve done an svn up (p4 sync, git pull, whatever) and the code you pulled down didn’t build. Or crashed in a super obvious place as soon as you tried to run it. Everyone? Yeah.
The reason for this is because most people are amateurs, and those who aren’t sometimes get sloppy. Shortly before I started working at VMware, they split the main perforce depot into three separate main branches. They couldn’t componentize the code, so they decided to componentize the developers into branches based on the organization they worked on, in some meager hope of making it so people could actually get work done instead of spending all day hunting for a changeset that wasn’t broken.
That’s right, with the most amazing group of hackers I’ve ever had the pleasure of working with, they couldn’t keep *main* building cleanly once there were more than a few hundred people working on the code. I’ve heard similar stories from many other engineering organizations that I deeply respect. If they can’t do it, what makes you think that you can?
Realistically, you can’t. Noone can. That’s why we have to take steps to try to prevent it. Steps like branching. And, surprise surprise, code review. And tools. Because if the build is broken, it’s not just you and your QA contacts whose time is wasted, it’s every single engineer you’ve got.
Why would anyone want to do it wrong?
So why do people do this? I’ve seen two common reasons.
REASON NUMBER ONE! “Because that’s how people are used to doing things.” This is a powerful reason, and I won’t deny that it’s very hard to get people to change their ways. However powerful inertia is, it doesn’t mean it’s right. It took a lot of effort to get doctors to start washing their hands, but I’m pretty happy that it changed.
Number two, and this is the big stupid one. “Because we’re a fast-paced development shop, and it would slow us down.” I’ve heard this from several people. It’s particularly hilarious to me, because they talk as if slowing down is a bad thing. How many MP3 players hit the market before the iPod? How many search engines came and went before Google? Being first to market is great, but being best to market is better.
And finally, a silver bullet
So how do we solve this big morass? Simple. Review code before it’s committed. If you’ve got branching that doesn’t hurt, commit to a branch and review it there, but before it affects anyone other than the original developers. Slow down, pay attention to what you and your teammates are doing, and you may find that you’re not quite so good at what you were doing as you thought. And once you’ve realized that, it’s hard not to want to take every possible step to improve.
Finally, before you comment, keep this in mind: I intentionally wrote this in a confrontational tone for my own amusement, and to encourage people to talk. I’ve been doing this software thing for a while, but I don’t have anywhere near all the answers. Think I’m wrong about all of this? Let me know!
#1 by Graham Binns on August 8th, 2008
Quote
This is exactly the approach we take in developing Launchpad (http://launchpad.net).
In addition, we use a test-driven development strategy (your code will get rejected by the reviewer if it isn’t sufficiently covered by tests) and, just to make things as watertight as we can, we use Patch Queue Manager (http://launchpad.net/pqm) to manage commits to mainline. PQM accepts merge requests by email (there’s a bzr plugin that allows you to send these from the command line by doing ‘bzr pqm-submit –m “Commit message”). Before it commits anything to mainline it runs the entire test suite on a copy of mainline with your changes merged in. If the tests fail, no commit. It’s a great way of ensuring that we *always* have a working mainline for Launchpad.
The downside is that because testing is comprehensive the bugs that still creep into the code (which of course they do; we’re only human) tend to be obscure, complex, niggling little ones that take a while to solve. But it beats having a non-building trunk.
#2 by mini_me on August 8th, 2008
Quote
Where I work we have a few hundred devs checking into one three and we rarely have build breaks. This is because check-ins are submitted to a gate machine which takes the patch, applies it to trunk, runs an “build verification test” and only checks it in for real if it passed the test. We don’t have all our tests on there but enough of them to prevent breaks. When we do have breaks it always a limited part of the products so one feature team has a very hard time causing down time for anyone working on other features.
We also do code review but it’s more a learning tool and not a mechanisms to prevent build breaks.
#3 by James Henstridge on August 8th, 2008
Quote
Having worked on some large projects at Canonical, I’m not offended by your post
Here are a few points from my experiences:
1. Use branches. Committing changes is a great way of doing work, but don’t put partially complete code on mainline.
2. Do pre-merge reviews. Once you’re using branches, the review question becomes “pre-merge” or “post-merge”. As you’re doing your work on branches there isn’t the same urgency to get things merged, so pre-merge reviews don’t block you.
3. Keep branches small and focused. Review time does not scale linearly with the size of the change, so you’ll get better reviews with smaller changes. If a change really is large, it can be broken down into smaller incremental branches that can be reviewed separately.
4. Keep the trunk pristine. As mentioned, having the trunk fail to build is lame. At a minimum, force a test build for each merge/commit to the mainline and preferably a test suite run. This is tedious but easy to automate, so that is exactly what we do with PQM (https://launchpad.net/pqm) — no need for the developer to wait around for the tests to run. Ideally any mainline commit can be a candidate for release.
5. Budget code review into the cycle. if you are writing more code than you can review then you’re doing it wrong. Find ways to get more of the team reviewing code, and give credit to the reviewers when merging.
We do use DVCS for all this, but it is not a prerequisite. For a globally distributed team it is certainly very helpful though …
#4 by fraggle on August 8th, 2008
Quote
Frankly, I think there are far more serious issues with the ReviewBoard UI to fix up than this one.
#5 by Travis Reitter on August 8th, 2008
Quote
How does merging work with pre-commit review? Ie, say two different changesets are based on the same revision and they get reviewed before either is committed.
Do you just assume that the merging will be trivial enough that the vast majority of problems, if any, will be trivial build issues?
Great article!
–Travis
#6 by Pete on August 8th, 2008
Quote
It’s never been clear to me how review-board would fit into a DVCS model where every feature/bug fix is handled in a separate branch. Here, you can do “pre-merge” review, which *does* have a associated branch that could be used for diffing against mainline, etc.
Has any thought/work been done to better support that workflow? Some way to define a mainline for the project, and let review happen by requesting review of a branch vs. the project mainline?
#7 by David Trowbridge on August 8th, 2008
Quote
@Graham: yeah, but the thing to remember is that those tricky bugs will be there anyway! If you’re spending your time finding those instead of battling the obvious ones, you’re winning.
@mini_me: that sounds nice, but doesn’t really scale. A full build of VMware Workstation across both platforms can take 5 hours in our sandbox. I think our full regression suite takes about a week to complete. That said, having some sort of tinderbox/buildbot is still awesome!
@James: great advice!
@fraggle: Yep. Workin’ on it.
@Travis: most of the merging is trivial enough that it doesn’t require additional review. In those rare cases where the two patches conflict in a complex way, whoever does the merge (the second committer) will often post an updated review.
@Pete: We’re slowly working towards a better workflow with DVCS. We now have support for a “base diff”, which lets people review changes which are based off of versions that aren’t necessarily in the repository that RB looks at. As far as a raw “pre-merge” review when you’re about to push to a central repository, that’s pretty much how I work every day; I’ve got a local git repository cloned from our perforce depot, and I do all my local development in branches there. When I’m ready, I push the diff for a change to RB, get it reviewed, then submit it to the depot.
#8 by Erwan on August 8th, 2008
Quote
I totally agree!
Where I work we started with no review at all (because the project and the company were young, we didn’t really have a process) to post-commit review (that was pretty much no review) to finally pre-commit review.
The code quality is now much better, and it doesn’t slow us that much: the time we spend in doing code review, we gain it by having less bugs and less regressions.
One very positive thing about pre-commit review is that developers know that their code is going to be read, and if it’s not good they’ll have to start again. So people are much less likely to do evil hacks to get the job done quicker, and they try harder to do stuff the right way from the start.
#9 by jba on August 8th, 2008
Quote
We actually take a completely opposite approach to this.
We get our devs to commit to mainl-line trunk early and often in order to ensure a full history of what is happening to the system is being recorded. We do this with CI approach, so there is an automated buildbot building and running tests on each trunk commit.
For our review proces we basically take the issue, the final patch set (sum of all commits for the one particular issue) and turn that into a final approved change. Traceability from issue, to changeset to review is a must , to be allowed to tag for release
What I’ve found in the past is that when devs are allowed to work on their own branches for each issue fix, they seem to fall into a pattern where there is complete disregard for system wide integration and substantial bit-rot in old branches that never quite got merged back into the mainline.
#10 by Benjamin Meyer on August 9th, 2008
Quote
Nice put together post. I have also been spending a bunch of time thinking about the idea of code review and came to similar conclusion that branching and review of branches before merging them into the main branch will lead to more stable main branch. Just last week I finally sat down and wrote up a similar blog entry detailing different code review types and my recent experience with code reviews and dvcs.
http://benjamin-meyer.blogspot.com/2008/08/code-review-should-be-free.html
#11 by Tobu on August 11th, 2008
Quote
Re DVCS, the kernel model is a good inspiration.
People put their patches on the relevant subsystem mailing list, those get reviewed using mail/news clients, Reviewed-by: joe-reviewer@example.com is added to the commit message (Acked-by for simpler fixes), and the e-mail, which is also a patch, is applied by whoever is the subsystem maintainer.
#12 by kaefer on August 13th, 2008
Quote
We’re looking to start implementing a formal code review process and I’m not yet convinced on pre-commit review. jba’s concerns echo mine — pre-commit review would seem to bias developers to holding off changes until they are quite large, at which point integration becomes problematic.
Also, there is the question of backup and the undo that occurs with incremental checkins. We don’t backup developer workstations — instead we back up the source code tree. With many small incremental checkins, a hard drive crash takes out at most a few hours work. With a larger checkin this wouldn’t be the case.
Am I missing something here?
#13 by David Trowbridge on August 15th, 2008
Quote
@kaefer: In my experience, pre-commit vs. post-commit doesn’t have any appreciable impact on the size of the changes, which seems to be mostly a personal thing. If anything, it makes them smaller instead of larger; a change with twice as many lines altered will take more than twice as long to review, and at some point (usually the second or third time a developer “blocks” themself waiting for review on a giant change), they’ll start breaking things into more manageable chunks.
If you’re worried about backing up pending changes, one thing to note is that once you post a diff on reviewboard, it’s stored on the server and can be downloaded in unified form, so if you’re backing up your RB database, you’ll be able to get the patches back even if a developer box catches on fire.
In the end, size of patches is a social problem rather than a technical problem. If you make it clear that smaller changes are ideal (and allow people to manage their own toolset, using things like quilt/git/etc to facilitate that), it’ll be easy.
#14 by eMBee on September 12th, 2008
Quote
how is pre-commit code review supposed to work with DCVS?
DCVS encourages you to commit early and often. on a good day i make a dozen commits or more. saving each step as i go along working on my task., none of which will ever seen by anyone else until the task is done. do you expect each of these to be reviewed? i agree with you that code should be reviewed before it touches anything that is shared by others, like commits or merges into the trunk or shared branches, but other than that, the notion of pre-commit codereview is simply not compatible with DCVS.
greetings, eMBee.
#15 by David Trowbridge on September 12th, 2008
Quote
In DVCS, “pre-commit” really means “pre-merge”. You can commit locally all you want, because that’s your development sandbox. As soon as you want to merge anything into a codebase which is shared between developers or used by anyone for real work, it needs to be reviewed.
#16 by noah on September 18th, 2008
Quote
My company has started doing post-commit reviews recently (by checking the svn logs regularly, and emailing comments based on those diffs — not a good process, but a start). We are considering using Review Board, and whether to do pre– or post-commit reviews. Here are the problems we see with pre-commit reviews:
1) Each repository commit should include code for just one bug fix. If I fix a bug, post a review but not a commit, and then fix another bug before the first is reviewed, how can I commit them separately? (Assuming both bugs affect the same files).
One solution is to save the first bug changes as a patch and revert my working copy; another is to use separate working copies for each bug fix. Both of those options are needlessly time-consuming.
A better solution would be committing the code to some kind of holding pen that doesn’t actually affect the development branch. Is that possible — aside from using personal branches — in subversion? Maybe via pre-commit hook scripts?
2) Other developers can’t use the code until it has been committed (at least, not without passing patches around). I guess that could be considered a good thing at times, but not at others.
#17 by David Trowbridge on September 18th, 2008
Quote
@noah: You raise some good points.
1. This is indeed a failing of SVN. The best two solutions I know of are quilt (which allows someone to maintain a stack of patches) and git-svn (which gives a developer ridiculous amounts of power with local branching). Review Board’s post-review tool natively understands git-svn checkouts, since both Christian Hammond and I use that for Review Board development.
2. If you really have immediate dependencies between developers, chances are you’re not splitting tasks up into orthogonal pieces well enough. Most of the time, even large features can be split up to minimize dependencies. That said, there are times when two or three people need to work on exactly the same thing at the same time. In these cases, the best solution I know of is a private branch, with pre-merge reviews.