Archive for July 2009
Code review is code use
Jesse Gibbs at Atlassian sent me to the following post from Scott Bilas at the game software company Loose Cannon Studios. In Peer Code Reviews: Good Commenting Practices, Bilas says code reviewers should seek architectural issues, and adherence to good software development practices and coding standards. And that they should look for mentoring opportunities. At the same time, he lets them off from other responsibilities, saying “Reviewers aren’t expected to catch everything,” and, “Reviewers aren’t expected to catch deep or systemic design problems.” |
It’s a pretty good tutorial on the current best practice of code review. So why does it feel like something big is missing?
Upon reflection, it turns out that, unlike, say, editing and commenting on a book, code review is not really a reviewer-author relationship at all. An editor may make a lot of changes, but those changes end with publication. Code, on the other hand, will be fixed, extended, and refactored by future developers. Each future, or “next” developer will need exactly two things from the code in order to do his or her job: readability and understandability. Code is readable if it has the right balance of abstraction vs. detail at each level of the code. Code is understandable if the reviewer can, without help from the author, see what the code does, and why. If code is clear, that is, readable and understandable, modifications will be easy and error-free.
So the question “what are the responsibilities of a code reviewer” turns out to be a trick question. Like this one, from driving class:
Question: In the following traffic diagram (imagine any diagram you want), who has the right-of-way?
Answer: Nobody has the right-of-way. Right-of-way can never be possessed, only given.
The code reviewer does not have any review responsibility. The code author has all the responsibility to write clear code, and provide it to the code reviewer. We should stop calling that person a “code reviewer”; instead, say “code user”.
Code review becomes a simple and easily explained two-step process. First the “code user” reads the code and notes down what s/he doesn’t understand how to use, and second, s/he meets with the code author to present what was not clear. In that manner, the “code user” successfully stands in for the “next developer”, and gives the code author an early chance to make things better. As a bonus, all the things a traditional code reviewer was supposed to check, or at least the important things, will be checked. Mentoring will happen. All this when we realize that code review is code use.
From Success to Failure: Problem-Solving
Someone comes to me with a problem that sounds like,
“I wanted to do X but it doesn’t work.”
When I hear that request, often they know a lot more about the technical details than I do. I need them to teach me in a way that lets me put things in order. My method starts from success … and progresses to failure. Strange? But it works.
The questioner is focused on the failure scenario. I crave success. There must be some around — just have to find it. I ask what similar scenario succeeds. What version of the file did work? What build compiled successfully? When did this test last pass? As we discuss the successful scenario, I learn why it worked. What requirements were met by that configuration. I repeat those requirements out loud for confirmation.
With a success in hand, well understood, we have a checklist to apply to the failed configuration. We can go over the failure and check whether all the things that made something similar work are present. Of course, one will be missing. We have found the problem. I may not know how to fix it. But usually the questioner does.
Thinking about automated testing
Google’s “Mr. Automated Testing”, Miško Hevery, talks about Software Testing Categorization. From the title, it doesn’t sound like much, but it prompted a lot of good discussion. Some excerpts on key points, edited for length and clarity:
Know what you’re talking about
You hear people talking about small/medium/large/unit/integration/functional/scenario tests but do most of us really know what is meant by that? — Miško Hevery
Need for speed
Let’s start with unit test. The best definition I can find is that it is a test which runs super-fast (under 1 ms) and when it fails you don’t need debugger to figure out what is wrong. — Miško Hevery
Make slow tests faster by running them in the background on spare machine cycles. Works for static analysis tools too. For example, Riverblade’s Visual Lint add-on for Visual Studio and Gimpel Software’s PC-lint. — Talk About Quality
I heard a senior test manager present at a conference who espoused the sophistication of his automated test regime (on a mainframe no less). Countless tests ran every evening, unattended. Lots of clapping in the audience. In the break I met a guy who worked for the presenter. He asked, “do you want to know why the tests run so fast? We took out all the comparison checks”. — Paul Gerrard
You’re right to have a healthy concern. Whether automated tests run in a millisecond or in several hours, they always have to be re-reviewed to see if they test something useful. Passing tests are especially dangerous. Everyone thinks green is great so they don’t look at the tests to discover that features have moved on and the tests don’t test anything. Automated tests are like automatic shift (does anyone still drive stick like I do?): very nice and we take it for granted, but when you press on the gas you still have to look where you’re going so you don’t cause an accident. — Talk About Quality
Test Planning applies to automated testing too
At my previous employer, the standard unit tests took upwards of an hour (for fewer than 2000 tests). This discouraged developers from running them, which resulted in broken builds for everyone. When we worked on fixing this, mock objects provided most of the salvation. However, a good portion of the tests were really integration tests and by correctly identifying them as such, we were able to remove them from the standard suite, and instead run them nightly. What Misko is missing from his post is how test classifications go hand in hand with scheduling tests. — TheseThingsNeedFixed
Play your cards right
A new project is ramping up and needs more resources. Another project is winding down — maybe it can do with less.
Let’s move some people from one team to the other
What’s wrong with this common resource allocation decision? It’s not the decision. Moving resources to the project that needs them makes sense. What’s wrong is the words, which hide a false assumption.
Here, “team” means location. Move people from one place to another. Like conductors putting people on a train: the “teams” are two train stations. One called “Project A Station” and the other one, “Project B Station”.
But teams are not train stations. A team is not the room it sits in, or its box on the org chart. A team is the particular combination of people — their skills and personalities — and their shared experience. They know their project or product best right now, and equally important, they know each other and the special role each person fills. Reassigning people from one team to another is more than moving a person. It is actually dismantling the first team, and disturbing the second. The movement of people based on the train-station model of teams leads to a contradiction: both teams — origin and destination — cease to exist.
If we must model people and treat them as resources, a better model is a hand at cards. The managers are the players and partners; the game is getting more projects completed. There’s a fixed deck — the “human resources” of the company. Some cards may be similar in strength or level, but every card is unique.
The value of your hand is based entirely on the combination of cards you’re holding right now. If you or your partners need a stronger hand, everyone knows that some cards will have to be exchanged. Put down 2, pick up 2. I pick up what you put down. Resources. Cards. I know this doesn’t sound any better to workers than “counting heads”, but it is. Because every card player thinks twice before putting down a good card, and prefers to take over an entire good hand if they can.
Do You See What I See?
Recently I found an excuse to dust off some old lab books of introductory science experiments. They were the curriculum for a 9th grade Physical Science I class at my high school; I wrote them as a summer job during college. I didn’t invent the experiments. Rather, the head teacher gave me outlines, and my job was to try them out and make sure they would work, so that prospective students wouldn’t get frustrated and turned off by science.
The excuse was a friend, a scientist, who is meeting a public service requirement of her studies by volunteering to teach science to schoolchildren, and to their teachers. I offered my science course as possible material for her classwork. As we looked over the experiments, we doubted a bit how even 9th graders might learn from them, or be as excited about them as I was when I built and tested them myself. Sure, the projects were easy, and they had worked. But would the experience of building these projects — static electricity demonstrations, electromagnets, motors — be enough to convey the principles behind their operation? Would the kids see the point?
Yesterday I was in the kitchen preparing a simple lunch of tacos and beans. Washing and cutting lettuce, chopping an onion, peeling a cucumber, grating cheese, warming the beans with some tomato sauce. In the quiet afternoon, I couldn’t help noticing the crunch of the knife through the lettuce. Or wondering how best to preserve the other half of the cucumber, now leaking water from its open end. And why do we grate cheese? So much science, here in the kitchen!
Now with the internet, you can search “science in the kitchen” and get pages and pages of websites with all sorts of neat science projects in the kitchen. Let alone YouTube with some pretty exciting and dangerous experiments. (Be warned!) I have no doubt that school science textbooks have taken this to heart and now include experiments that relate to the real world. And yet, science enrollment declines.
Even with the best of intentions, popular science experiments in the kitchen won’t do it. Science is not in school, nor is it in the kitchen. Science is not an activity, but a way (just one way) of looking at the world and making sense of it. What excited me in science class was not the sitting and listening to the lecture. It was making the connections with daily life outside class, and enjoying the beauty of the natural world with new understanding.
Those understandings could come from things as simple as basic cell structure in biology. There’s lots of water in a living cell, so when you peel and cut a cucumber, it gets wet. Or as complex as thermodynamics. One college winter, I learned that there’s really never a flow of “cold”, but only heat transfer. For a good month after that, climbing the steps to class, I would grasp the banister outdoors, and instead of feeling cold, I felt the heat flowing out of my hand into the metal. These experiences are what brought me back to study even more.
Science teachers have to have and share that excitement. If they’re not science experts themselves, no matter. They are learning adults, who can build their own understanding and catch the excitement from science mentors. The key has to be in giving the right answer to the question so many students ask: “But what is this good for?” The wrong answers are the allegedly practical ones — advancing technology, getting a job, or passing the test. The right answer, the one that should guide science teaching, and bring the kids back to class, is “Because the world around you is beautiful, and science gives you eyes to see it.”
Product Quality: 3-in-1
It’s pretty well known that there are three ways to improve software quality: improve process, improve methods, and improve tools. But these are the generic concepts — applicable to any software development effort. Today’s topic is more specific and closer to home. Product quality.
Isn’t it obvious? The software product is the running software, and product quality is how well it meets its requirements. Simply put — does it do what people expect it to do, and not crash or lose data?
But organizations which release software focusing only on that one aspect of product quality are often unpleasantly surprised by inconsistency. They are doing all the right things — following a good process, using good methods and tools, and regularly reviewing all those to see what can be improved. Yet sometimes the product is better, sometimes worse. Why?
External product quality — in software, that’s how well the software serves its users when it’s running, is not enough. There’s also internal quality. The readability of the codebase. Code that’s well-written — easy to understand quickly — is easier to maintain. Fewer programmer errors, and fewer schedule slips. We can add to that clear and organized debug output, which avoids false positives, and gives an accurate record of when the running software seemed to be fine, but was really operating on the edge — just barely recovering from failures.
But there’s another part of the product that’s often overlooked. Overlooked when reorganizing teams, or just when parceling out the daily assignments from multiple projects. The developers. If you take a single iteration of a software product, with development at its best, you’ll apply best process, methods, and tools to put out the next version of the software, and maybe even have codebase improvement (cleanup, refactoring) on the list too. Get to release and you deliver good running software, more maintainable code and … the new state of the team that developed it.
The team members may have learned new methods or technologies. Or just how to avoid pitfalls from the past. Equally important, that particular team learned how to work together — not in general, but specifically in today’s process, methods & tools, and codebase. And with each other. This third part of the product cannot be ignored or thrown away, just as nobody would think to delete a code branch delivered to the customer, or switch out a working library in mid-project.
Looking further at this third component of the product — the team that delivered it — we see clearly that those quality improvement concepts of process, methods, and tools do not exist in a vacuum. They are not just abstract ideas. Rather, they exist in the team members who develop a product, and are unified by teamwork — which means this team learning to work together with these people on this product.
So when you think of product quality improvement, plan to improve all of these:
- How the product works
- How the codebase looks
- How the team works
And at the end of an iteration, make sure to review and protect all three. That’s the guarantee that the next version of the product will be even better.
John Henry the Maintenance Programmer
Thanks to Bulkan Evcimen for tweeting about an article I doubt I would have found otherwise: “A Genetic Programming Approach to Automated Software Repair” (pdf link here), by Stephanie Forrest et al. They describe there how they fed a genetic programming algorithm the following inputs:
- Source code with a known defect
- A negative test case
- Several positive test cases
The authors show how software with the genetic algorithm is capable of repairing the defect such that the software under test no longer fails, while it still passes the positive test cases. They complete the activity with additional software that reviews the change and reduces its scope to the minimum.
Is this the end of the maintenance programmer? If so, this time there would be a happy ending: John Henry lives, and goes on to be a railroad engineer.
The Only Valid Measure of Code Quality
It’s Thom Holwerda — keeping things simple for us:

Simple Code Quality Metric
Here’s his site — with a search for “code quality” (warning: if you can’t ignore “Evony” web game ad graphics, stay away).
Cancel that!
A medical radiation machine operator types the letter ‘x’, realizes it’s an error, backspaces, types ‘e’, and continues. Consequences of the error and related defects lead to the patient’s untimely death.
Ten years later, a Japanese stock broker mistakenly switches the share price and amount on the sell order of a new stock. He tries to cancel the order, but fails. His employers lose $225 million, and are involved in lawsuits for years.
A blogger clicks the “back arrow” by mistake, gets a warning message, concludes he meant to continue writing, and clicks “OK” to continue. He loses his work, and has to rewrite it. Here, the consequences are annoying, if trivial by comparison.
What do all these cases have in common? Canceling a request. If it’s not hard enough to program computers to do what we want them to do, who would have thought that telling them not to do it would be hard too?
The cancellation scenario — or “use case” as it’s called in software design — is the silent partner of every positive request supported by a piece of software. It has to cover giving the user clear options, executing the cancellation, and rolling back any partial results. Things get more complicated if authorization is required, or if the transaction has already gone through (both of those requirements figured into the Japanese broker error story). Cancellation and rollback are also part of automatic requests that may occur if one software module (the “server”) cannot complete a request by another (the “client”) and has to make sure to put everything back the way it was, and send the proper response code.
So the next time you’re designing a piece of software, no matter how simple, think what it’s supposed to do, but also what it will do if the user or client calls out, “Cancel that!”
Software’s Unwelcome Advantage
You can do anything in software. Hordes of eager computer science graduates, not to mention brilliant open source coders, keep joining the ranks of software development because it’s fast and fun.
The fact that software is instructions to a machine (the “hard” in “hardware”) seems to have been the only thing John Tukey had in mind when he coined the word “software” back in 1958. (Dr. Tukey, an accomplished statistician, was more focused on computerizing the Fast-Fourier Transform, and criticizing the Kinsey Report for its questionable sampling methods.)
While software attracts developers with its ease in creating things, it tempts us all with its other “softness”: amenability to change. Software can be endlessly fixed, extended, improved. And that advantage demands something of developers which was unexpected, and, well … hard.
Hardware must fit form to function so it’s easy to use. And come with good documentation for maintenance and repair. But that’s all on the outside. Software, always ready for change, has to be clear and readable on the inside too. In other words, software developers have to be good writers, because the next developer will have to read and quickly understand what’s going on in order to change it. Written changes, again, have to leave the software in a clear and readable state.
In high school, I avoided English class like the plague (and got bad marks for using cliches in my papers), preferring to go into school on snow days to use the (one) computer. Good writing is not why people become programmers. But it’s exactly what we need. Clear written communication. Equal in impact to life-changing books (pen mightier than sword and all that), software crucially affects our lives — from cars, to food transport, to the electric grid.
That good writing is an unwelcome requirement of sofware is why developers code quickly and obscurely, hate documentation, and shun code review. And why managers push for features, delivery, and fixes, while devaluing internal quality.
Is there hope? The only one I can think of must exploit other likes and dislikes: managers want software changes fast, while developers like writing new code more than fixing someone else’s (or their own) bugs. Good writing is the only way to make code support that scenario, and reap the advantages of software.