The idea was brought up of using comments rather than actual function call stubs, and then exploiting IDE tools like global find or Visual Studio's Task List to locate and flesh out those comments at a later date.
This isn't a particularly bad idea, per se, but it misses the spirit of what I was trying to suggest. There are a few major shortcomings to this approach:
- It doesn't integrate with test-driven development. Function stubs can be set up for TDD immediately. Moreover, you can adjust the tests of the calling code to expect (or not expect) the stubbed function to work. This provides close support for regression testing and other quality-control measures.
- Commenting scales poorly - suppose we have 1 function that gets stubbed in, and 200 calls to that function. That's 200 replacements we have to edit into the code; if we'd used a function stub, we'd simply have to write the function.
- Using comments still places the burden of updates on the client calling code, rather than the called code.
This last point is really the Big One, so I'll spend the rest of the entry digging into why it's such an important (but subtle) distinction.
First, to clarify: this goes back a bit to the scaling issue. Let's take a look again at our hypothetical function, which is called 200-or-so times. If we use comments, we have 200 locations that have to be updated. If we format our comments intelligently, we can automate the replacement process. However, that actually introduces more work than just stubbing in a fake call.
Here's some examples to illustrate what I mean:
// Commenting methodvoid DoFoo(){ Baz(); ALittleBitOfQuux(); // STUB: Xorph (20%)}// Now we just need a regular expression or something// to convert that call to a Xorph(0.2) call when we// get around to implementing the Xorph function.// Stub methodvoid DoFoo(){ Baz(); ALittleBitOfQuux(); Xorph(0.2);}void Xorph(float percentage){ // TODO: implement Xorph functionality}
Using the commenting method has some problems. First, there is nothing ensuring that all stubs are consistent. Using the stub method is superior because we can exploit the compiler to make sure everyone treats the interface the same.
Secondly, the commenting method introduces the overhead of the search/replace step. There's really no need to add this extra work, since we can just write the stub calls the first time and be done with it.
Lastly, since the comments are not using the compiler to enforce a consistent interface, we can't find potential problems in the interface until later. Suppose we comment-stub our 200 calls to Xorph, expecting to use a floating-point percentage value. Later, when we implement Xorph, we discover that some calls really should be an integer scalar from 0 to 1000.
If we had used the code stub method, we would have learned this sooner, and introduced an overload for Xorph to properly handle the two different cases. However, with the commenting method, we're screwed. We literally will have to go back through every case to make sure it uses the proper call (float or integral). Now we're worse off than when we started - because, as you hopefully recall, the original problem was that we wanted to avoid going back through all the Xorph client code in the future!
In closing, I'll revisit the Big Point from earlier: using stub code rather than comments shifts the burden of effort very significantly. Instead of each client call being a separate focus point, we divert all of the focus directly into the stubbed module itself. This concentrates the amount of effort and work we have to do into a single location. And, as I talked about earlier, the smaller chunks of information you have to keep in your mind at once, the better your code will be.
So hopefully that clears up why I specifically suggested what I did, rather than using comments. Thanks to everyone who dropped in for the discussion; it's always great to see feedback [smile]
This post pretty much matches my point, but it might be worth mentioning that this practice really shines in larger scale projects and code interactions where comments are simply unwieldly.
I had the good fortune to be part of a project where we had a few teams working on seperate but interdependent parts of a system. We decided to stub out the 'interaction points' using (quite complex) interfaces for the classes the code would share across parts. These interfaces were then implemented by placeholder objects that would give back somewhat meaningful data for preliminary testing. This way each team could work on their own stuff, confident that everything would work together smoothly as parts were combined, while quickly spotting incomplete or impractical interfaces as the project progressed.