Why code review beats testing: evidence from decades of programming research
kev.inburke.com
kev.inburke.com
- Code reviews are an effective means of knowledge transfer among engineers.
- It makes engineer more conscious while writing code since the patch will be read by others and hence less likely to cut corners/put hacks.
- Since the patch should be understood by others, author needs to ensure the code and the change/commit description are well documented.
- It lets your co-workers/manager know about the amount of progress made on a task.
- Provides a mechanism for developers to receive feedback to promote learning and improve code quality.
I'd also add something: "knowledge transfer" not only means that more experienced programmers teach techniques to newer ones, but also that we knew the code for all the projects in the team. In case someone leaves or is transfered to another team, it's pretty easy for other programmer to take over.
Edit: also it adds a way to control that people are working, both by the boss and peer pressure.
http://www.infoq.com/presentations/francl-testing-overrated
http://railspikes.com/2008/7/11/testing-is-overrated
http://www.scribd.com/doc/8585284/Testing-is-Overrated-Hando...
Code reviews are great to ensure a consistent code base that follows a certain coding standard. It is also great for passing on the knowledge between developers (avoiding common pitfalls, better ways of doing things, etc...)
Testing is such a big field that it is hard to compare directly with reviewing code. Manual tests help in verifying the user experience and end to end scenarios, esp for UI (something that code reviews can't do). Automated functionality tests are great in verifying daily builds and catching regressions (which is also very hard to do in code reviews in fairly complicated systems). Various other types of automated tests are necessary for different considerations: performance tests, security tests etc...
Also I'm quite curious how you can actually count the number of bugs found from each of these methods. Test-after unit testing would even be preventative if you wrote tests immediately after writing the code. If doing that made you think about more edge cases and thus fix up the code then it seems like those bugs would not get counted.
Seems like many of these would have different amounts of a preventative effect that would be difficult to measure....especially if you wanted to isolate their effect from each other.
In addition they went to larger companies, checked which bug detection techniques they were using and how well each one detected bugs.
I have the gated PDF, email me if you want a copy.
That said, preventing regression is tremendously important. Especially after some turnover on who works on the code.
I think that's a little backwards, shouldn't it say less expensive?
I agree that it's hard to tell which is picking out more important bugs. My guess is that less prominent papers by the same authors get into the topic in more detail.
Anyway, has anyone applied "modeling" or "prototyping" to mobile development?
You can do paper UI prototypes with good results, they will make you think more about the corner cases, which in turn will do good to the code quality.
When we were making games I used to prototype some of the algorithms in Perl to benefit from the faster development cycle and expressivity (being able to quicky hack whatever conditions needed).
And of course prototyping is a perfect way to try the high-risk parts of the system first, so that you don't spend too much effort only to hit a blocker later in the project. But that's off the topic of quality.
I wasn't implying that I would simply run unit tests instead of testing at the end of being code complete. I want to arrive at being code complete a little closer to shippable.
There are two anti-bug ideas I strongly agree with: bugs are found by use of the code, whether through testing or actual use; and that great unhindered programmers tend to write solid code. It's very logical that bugs will be found by usage - this is basically the test-driven reasoning. As far as programmer quality, it is also logical, but it is underemphasized in the article.
I distrust articles that emphasize one-size processes for software development. In the end, there's talent and something like culture. Government agencies that can't go out of business follow policy, and provide results just above the legal required standards. Old companies with seniority-based hierarchies feel similar to code monkeys. A small company with respectful, productive, and creative builders is ripe for a culture of sincerely-desired quality. This is what I mean by the importance of culture over process.
Where process definitely helps is in finding those bugs that can't be found "by use of the code." i.e., the bug will be experienced by the user, but it may be invisible to the developer. In just the last year, we've uncovered two nasty race conditions that have been in our codebase for almost a decade, but only just started to show up because some unrelated code changed the timing of certain behaviors. Even having knowledge of the problem (thank heavens for good log files!!), we could not design tests to verify that the bugs were fixed: the windows of opportunity were just too small. Code inspection was the only way to verify that the fix matched the problem.
'Giving programmers a specification, measuring how long it took them to write the code, and how many bugs existed in the code base'
Like another poster said, this was just linkbait. Would rather see a more reasoned analysis.
It does make that one statement about code reviewing being faster at finding bugs, that testing. But it discounts TDDing (i.e it looks at programs that already have bugs, and times how long it takes the programmer to find them).
So code reviews are faster at finding bugs than testing, as long as you discount a number of testing techniques.
I think there are cheaper ways -- along the lines of automated testing and design reviews in lieu of code reviews -- to reduce risk and defects, and obtain high quality software, than to spend such massive time/$ on code reviews.
We use design reviews, code reviews, unit tests, integration tests and final validation tests. Our bug rate is well below 1/kloc, but bugs still get out the door.
In the end it depends on how you calculate Cost of Quality. In some environments, having a customer experience a bug can have disastrous consequences, in others it's not a big deal at all. We're in the former category :-(
It's really hard to do a (proper) code review, a programmer who is capable of it is probably a high caliber programmer (I assume programmers review each others code).
Also I don't like this sentence: "code reading detected 80 percent more faults per hour than testing".
If you look at the article that sentence links to you will find that the problem is poor technique in creating the tests. You can't do "white box" testing without reading and understanding the code in the first place. And if their "black box" testing didn't find bugs, they didn't write the test properly in the first place.
That's practically begging the question: a test isn't a real test unless it catches all possible bugs? Really, any code for which such a magical test can be written is probably so simple that it only exists inside Fizzbuzz interviews.
Years of experience have consistently shown me that 10 minutes of looking over code by another developer will find more bugs than days of testing. A huge number of bugs are obvious to the eye, but have high odds of escaping tests. Doubly so in code where there's no "right" output to test for: as in the case of any non-optimal optimization function.
This doesn't mean tests are bad. Tests are good for the sort of code that's easily testable, and large-scale regression testing is needed for any project, even if unit tests aren't feasible. But testing is no substitute for having people read the bloody code.
i.e. you known what the code is supposed to do then you test that it really does it. Then you think about edge cases and test those.
Maybe if your code is "Hello World", but what if your code is a motion search, or a facial recognition algorithm, or a band-pass filter, or any other sort of real-world code whose operation can't be summed up exactly in two lines of code?
As many samples as possible, and since you wrote it you know which samples are hardest for it, and which trigger edge cases.
I'm not saying never review code, I'm just not convinced that a programmer's own testing works so poorly.
What if the exact meaning of "correctly" isn't defined?
Here's what happens when you blindly apply this approach to real-world applications: you end up with hardcoded "correct" md5sums that are "whatever the function happened to output last time, and we checked and thought it was right". I've seen this happen, repeatedly, with real-world apps, and it is useless at best and usually harmful.
For a huge span of real-world applications, there is NO RIGHT ANSWER. You can't blindly apply unit-testing to that in the same way you would for a web framework.
Take the facial recognition example. Give the whole thing a few images that it should fail on, and some it should pass. As you learn more about the system and where things fail or just don't add up, add tests there. Its likelier those parts of the code are bad too so refactoring isn't unheard of.
To a degree I completely agree with you that some things don't have a easily known answer in the real world. But adding tests to test known things like regressions, just seems like it should be a minimum of testing. I can't count the real world apps I've seen without a single "proof" that they don't regress state as things change. And they do, often and repeatedly. But dealing with external products and teams can be frustrating.
I'm guessing there is a right answer, it just might be poorly specified. And automated or unit testing doesn't guarantee that even simple things are correct. It's just a trade-off of how much time you put into the automation versus the time you spend chasing bugs you could have caught with automation (which isn't all of them). I'd think in most real world code (for all the usual real world reasons) that people err on the too-little automated-testing side of things.
What do see as the problems with checking the md5sums? I'm guessing they're brittle if the answer isn't supposed to be exactly the same, but that reduces down to the same as checking a number is 5 when it might be between 4.8 and 5.2. It's not rocket science and getting it wrong doesn't invalidate all automated testing. At the very least the first time it throws an error the developer who wrote the bad test is going to learn something new about his system and you might sensibly use such cheaply written tests to locate errors when running code on a different OS (or version) or after minor cosmetic changes where you would expect the exact same result.
> or any other sort of real-world code whose operation can't be summed up exactly in two lines of code?
By this, I assume you mean real-world code whose operations consist of 10,000 lines of un-modularized spaghetti code.
That is the same thing that came to my mind. Indeed, if you write code like that it will be really hard to write automated test cases, debug, maintain, etc. etc.
Then you break the problem down as much as possible so that you know exactly what should be the expected output for the expected input. You have to keep breaking the problem down until it becomes obvious how to automate the tests. Otherwise it'll be really hard to write good, clean, modular code.
I find this surprising. Unless you are talking about new developers. Automated test cases should be more than unit test cases. They should include system wide test cases as much as possible. i.e. For a game you would write test cases on the physics engine. The physics engine includes collision detection and collision response. Of course, you would also have test cases for each of those modules independently.
static final String HELLO = "helo wolrd";
...
out.println(HELLO);
in test: input = in.readln(); // yeah massively simplified, I know
assertEqual(Code.HELLO, input);
pass.But a developer will (should!) find the error immediately.
I have no source on this but isn't it bad practice to use constants in your tests? I personally use a new string with the expected value. It forces me to look twice and really consider what I am doing.
The point is that it is possible to have good test coverage and still have bugs that are best found by humans.
I agree in principle, but your example is off. Optimization is testable, and even provable. If your optimizers doesn't rely on ad-hoc techniques, you can also usually give an estimate of how far away your solution is from the true optimum.