In a recent post to the XP mailing list, Adrian Howard said:
Many people seem to have a great deal of trouble going “meta” with mistakes. People will happily write code, discover it has bugs, spend half an hour with debugger finding bugs, fix bugs and repeat without ever saying to themselves “Is there a way I could stop adding those bugs in the first place”.
How often do you see this within your own team, and within yourself? I know that I am certaintly guilty of this. I’ll write something like:
using(SqlConnection conn = DataAccess.GetConnection())
{
using(SqlCommand command = conn.CreateCommand())
{
command.CommandText = foo;
command.ExecuteNonQuery();
}
}
So what’s wrong with it? It’ll fail every time because the connection is not open (unless CreateCommand() opens the connection when it returns it). And the normal thing I do is run the code (usually the test that caused me to write the code), stare at the failure, look at my code, smack my head, and add:
using(SqlConnection conn = DataAccess.GetConnection())
{
conn.Open();
using(SqlCommand command = conn.CreateCommand())
{
command.CommandText = foo;
command.ExecuteNonQuery();
}
}
Then run my tests again and watch them pass. Next, I’ll merrily go along the way, not wondering if there is something I could do to keep this from happening once and for all. As Adrian says: “It’s not really not wanting to learn, but not being able to see that an opportunity to learn exists.”
I see this too often in day to day dealings. A lot of developers just want to get something working, get it out the door, go home and be done with it. Not that I’m advocating against that – I like spending as much time as possible at home. But if you make the same mistake over, and over, and over again, what part of our minds doesn’t raise a flag that perhaps we should investigate /why/ we are making the same mistake.
Willie Crawford says in an article, “That next step that we often need to take is simply in the doing. Knowing is not doing. You can understand something as deeply as possible on an intellectual level, but until you do something with that knowledge, you’re no better off than someone who doesn’t understand.”
By I would take it one step back. I believe that often times people just haven’t experienced the same things, and therefore can’t see that mistakes are being made. In my above example, I just assume that I will always make that mistake, and remind myself to check for it. I may not have read an article, or a newsgroup posting, or talked to someone about the issue. And because of that, I don’t know I need to improve.
A good friend and fellow developer told me recently that he doesn’t need tests because his code doesn’t have bugs, and doesn’t need refactoring tools because his designs are dead-on. I admire that spirit, and hope that I am able to learn a lot from that. I, unfortunately, am not infallable. Give me a SqlConnection and I’ll forget to open it every time. But what if he has to interface with my code that surely has bugs in it? And what if all those tests that drove the design of that code help us track down that bug rapidly, and we are able to write a new test or two which seals that hole? Maybe I will get a convert to TDD that may not have otherwise happened if I wasn’t a second-class developer. After all, if your code just works, and you don’t have to modify it, Unit Tests and Refactoring tools are going to be useless to you.
So, I guess I’m glad I make mistakes, not just so I can learn, but so I can introduce a way of walling those bugs to others who might not otherwise have seen it.
Amazing how much you can get out of forgetting to open a connection!
One lesson I have learned is that disposable resources (db connections, files etc) are fundamentally different from other objects. Even though there are obviously incorrect ways to use them, poor compiler support means that we get no warnings when that happens.
Luckily, the primary rule when working with disposable resources is that you should “open them, use them, and close them”. If you think about it, unless you are using a broad range of the disposable resource methods, it is often easy to hide the resource completely away behind single method calls, so that your code does not need to know that disposable resources are involved at all.
(In your example, if you had a DataAccess.ExecuteNonQuery(foo) method, you’d be sitting pretty).
Hi Steven,
I agree with the concept of static calls, and we use it quite a bit. But darned if I still don’t forget to open the connection when calling it through that!
You bring up a very interesting point about compiler support for disposable resources. It is a shame that the compiler didn’t have some way of knowing that the connection should be opened before it is used.
Of course, I’m not a big fan of relying on the compiler to find everything anyway, but it is an interesting thought.
When I find a repeatable error, I make a code snipet or template for it.
Like my frequently used one in delphi:
var
pReturned: TIRSParam;
begin
pReturned := TIRSParam.Create;
try
finally
pReturned.Free;
end;
end;
This way I never forget to create and free up the variable.
Helps a lot :)
Sorry for the lack indentation…
Forgot the tags…