Code Complete

One of my early comportment report cards listed the following incongruous attributes; good learner, apt to attempt new things, poor handwriting, easily distracted and unsatisfactory follow through. My first review from a job that mattered to me had the following similar outlook; quick to adopt new technologies, strong design intuition, and poor follow-through.

Needless to say I'm at the very least, uneasy about my abilities when it comes to assessing the completeness of my work. Oddly I don't obsess over some qualifiable measure of the code's actual completeness rather its perception of completeness. To a "T" those reviews nailed my personality. I get bored, and fast, so it's a constant battle to stay focused on the tasks at hand. To help assuage my eternal guilt I focus in on code completeness traits like consistent formatting, commenting, clean up of unnecessary temporary variables, tidy class structures and the debatable practice of using section tags.

The problem I see is that there is no true standard of code complete. Every application I've ever seen written can be improved. From the simplest changes like renaming a field or method for clarity, to wholesale refactoring, if there's time and money, it can be better. Certainly you can apply the vague measures of conformance to requirements like functional, security and performance, but nothing in these specify that I can't leave the code littered with TODOs or known issues that are not covered by existing use cases.

Take for instance a method "ConvertFractionToDecimal" that needs to return -1.0 if the denominator is zero. I can satisfy that with the following pseudo code:

Decimal ConvertFractionToDecimal(int Numerator, int Denominator){
  Decimal result = -1.0;
  Try{
    result = Numerator/Denominator;
  }
  Catch{
    result = -1.0;
  }
  return result;
}

 

And while that certainly works it's pretty easy to see that this method would be better served by checking the value of the Denominator integer prior to performing any calculation. Of course completeness in the real world is not quite so clear cut. With every code refactor there's always a risk of changing the expected outcomes, and while a good array of unit tests will usually catch any changes in I/O there's always the possibility of introducing a new outlier condition that isn't covered. Often times we become so compartmentalized in our development projects, so driven by mechanical repeatable measurements like NCover and NUnit we fail to do the final check for the coding equivalent of homonyms.

So what's the point of my isotropic complaint? Well first I'd like to invoke the great arguments about testing and the focus of testers brought up by Stuart here. And then I'd like to refer back to my post from yesterday here, where I talked about checking my design against requirements to bring up the topic of Contracts. Contracts are the high fructose corn syrup of the software world; they show up everywhere and seek to sweeten everything they infest.

Contracts are from my prospective the best way to approach completeness as an ideal. In the foremost they are the test to verify that the code written obeys the contract to which it was developed. Does for instance the above ConvertFractionToDecimal actually return a decimal that is the numerical equivalent (or acceptable approximation based on precision requirements), and does the method return the expected results in outlier and error cases.

Additionally contracts can be used to define things like code formatting and placement; class structure; inheritance practices; unit test coverage. Basically the idea is to create formal or informal definitions of assessable code states. For myself I rely heavily on checklists formed prior to coding that reside like shotgun splatter on my desktop. I'll make a list of code "soft spots" to look out for, such as the above case where it makes more sense to pre-check for error conditions than use exceptions to manage the expected. I'll make a list of naming conventions such as always having an uppercase "ID" at the end of any identifier field. I'll make a list of API consumers, that might be just a list of use case numbers or a list of pseudo code calls. I make lists, or contracts and then I verify that I have fulfilled my initial design ideals.

Essentially between this post, the prior one and the next one, I'm trying to build a roundabout that encircles the principals of Scientific Method (again with the nod to Stuart's Falsifiability post ). In reality the scientific method is not truly directly applicable to the software development process, but hopefully by the end of the next post I will have a least made a "Fox News" worthy link between the two.

 

 

Print | posted on Tuesday, May 08, 2007 10:08 PM
Comments have been closed on this topic.