Thursday, January 15, 2009

Fixing a bug, more than a local hack.

This is the true story of a bug, and the process of solving it. For reasons not relevant to the discussion I had the need to determine if a given 'Type' was part of the "System" namespace and hence could be considred a Framework type, it was deemed enough that just checking that the full name began with "System." was good enough for our purpose. The implementation is obvious and self-explanatory:
static bool IsFrameworkType(Type type)
{
        return type.FullName.StartsWith("System.");
}
Simple elegant and to the point. But since I said this was the story of a bug something went astray. The thing is that under certain quite arcane circumstances "Type.FullName" can return null, the details aren't really important, the problem is that the code above crashes with a NullReferenceException if that happens. And that's important for me since then I won't get to see the result of my program run. The quick, obvious fix is to check for null. As seen below:
static bool IsFrameworkType(Type type)
{
        var name = type.FullName;
        return name != null && name.StartsWith("System.");
}
And that's how the majority of bugs get "fixed", a local fix at the point of failiure, a pat on the back for a now passing test and away we dart to the next challange. And that I think is part of the problem why software takes so long. If we keep our local focus what can we do to both fix this bug and design safety in? We can reengineer our API to handle this gracefully. In languages with open classes we could fix our string API, in C# we have extension methods and if we work in an environment without them utility classes and free functions can aid us. So what's the root cause here? I would say that "StartsWith" is the culprit in this case. Because we know that we will have something to check against "System.", but we're not sure that we have a target for our StartsWith call. For this situation "System.".IsStartOf would make more sense, since that way we know that we have a instance to start with. Using extension methods we arrive at this:
static class StringExtensions
{
    public static bool IsStartOf(this string prefix, string s)
    {
            return s != null && s.StartsWith(prefix);
    }
}

static bool IsFrameworkType(Type type)
{
        return "System.".IsStartOf(type.FullName);
}
Hunting through the code-base I found a couple of other places where this could be used to solve similar problems. How often do you take this extra step to not only fix the problem locally but ponder if you can modify the API or system to prevent it from happening somewhere else? Make it part of your routine, and you're one "why" closer to a clean and productive code base. Further analysis also made it possible to ensure that all types that eventually got sent to this function indeed did have proper FullNames. But that's another story.

No comments:

Post a Comment