Most projects I've worked on contained a sizeable number of compiler warnings. These are usually ignored by developers as being unimportant. So there are a few (dozen) variables defined that are never used - so what? Duplicate 'using' statements - that can't hurt anything...
It's true that some warnings are mostly harmless. The problem is that these warnings often hide other, more important ones. A few serious ones that come to mind:
- Unreachable code. Perhaps there's a return statement halfway through a function. Or a conditional that will always evaluate true (or false.) These often have unintended side effects.
- A variable is never assigned to, and will always have a default value. You've created a variable, foo, that you're using in your code, perhaps in a method call or conditional. The problem is, you never set foo to anything. It's possible the variable should be removed or replaced with a constant. But it's also possible you forgot to retrieve or calculate something.
- A class member hides an inherited member. Maybe you intended to replace it with your own. In that case, use 'new' to explicitly document the desired effect. If hiding was unintentional, you're better off renaming the member. Otherwise, you may later decide you need that hidden item. At which point you end up with a bit of code rework.
Wednesday, July 25, 2007
Tuesday, July 24, 2007
Single responsibility builds
When I joined my current project, the CI build server (CC.Net) was already in place. For the most part, things were in working order. One item I took exception to was the fact that every build copied files to the development server. The idea being that a developer can run tests to verify that all of the pieces are working correctly. For most of the team, however, this is instead used as a convenient way to code the UI without running the services locally. This makes sense.
The problem, however, comes in when a significant change is made to that code (as in the recent data access changes.) Much of the code still has bugs to work out, despite the fact that the code compiles. The way the build is set up, the copies to the server happen after the compile, regardless of the unit test results. The end result is that after the change, developers could no longer test against the server's build.
Ideally, the continuous build process would not touch the dev server files. Instead, a separate build is created to copy the files on demand (using a Force Build in CC.Net.) When the build is in a faulty state, UI developers can continue working off a known-good base, while others can iron out the bugs in the server code. Another option would be to fail the build if any unit tests fail, so the file copies aren't carried out. Of course, this assumes the tests normally pass.
The problem, however, comes in when a significant change is made to that code (as in the recent data access changes.) Much of the code still has bugs to work out, despite the fact that the code compiles. The way the build is set up, the copies to the server happen after the compile, regardless of the unit test results. The end result is that after the change, developers could no longer test against the server's build.
Ideally, the continuous build process would not touch the dev server files. Instead, a separate build is created to copy the files on demand (using a Force Build in CC.Net.) When the build is in a faulty state, UI developers can continue working off a known-good base, while others can iron out the bugs in the server code. Another option would be to fail the build if any unit tests fail, so the file copies aren't carried out. Of course, this assumes the tests normally pass.
Monday, July 23, 2007
Always address failing unit tests
I cannot stress enough the value of unit tests. Whether adding new features or refactoring existing code, you want some validation that you haven't broken existing functionality. Unit tests are usually the fastest way to accomplish this.
This is why it annoys me when unit tests are left in a failing state. In some cases, the tests are invalid and need to be removed. In other cases, they need to be updated to match code changes. Regardless of why a test now fails, it needs to be corrected.
On a project I'm currently involved in, there was a significant rework of data access code. As you can imagine, this introduced a number of bugs, many of which were caught by now-failing unit tests. This is a good thing - that's what the unit tests are for. The problem is that the 50 newly-failing unit tests are mixed in with the 40 already-failing unit tests. We need the new failures fixed quickly, but sorting these from the "junk" unit tests will take time.
This is why it annoys me when unit tests are left in a failing state. In some cases, the tests are invalid and need to be removed. In other cases, they need to be updated to match code changes. Regardless of why a test now fails, it needs to be corrected.
On a project I'm currently involved in, there was a significant rework of data access code. As you can imagine, this introduced a number of bugs, many of which were caught by now-failing unit tests. This is a good thing - that's what the unit tests are for. The problem is that the 50 newly-failing unit tests are mixed in with the 40 already-failing unit tests. We need the new failures fixed quickly, but sorting these from the "junk" unit tests will take time.
Tuesday, July 17, 2007
Debugging with Exception Breakpoints
How many times has this happened to you? You're working on code that's not quite functioning correctly. You suspect there's an exception being thrown somewhere, but it's being caught and ignored. Maybe this was intentional, or maybe it was poorly written code. Either way, you need to locate the problem. Visual Studio 2005 has a handy little tool to help - Exception Breakpoints.
Say you have code that looks like this:
If you were to run this code, you'd never know that there was a problem (well, not until you tried to use the value of 'c' elsewhere.) What we want to do is have the debugger break as soon as an exception is thrown. To do this, open the Exceptions dialog, either through the menu (Debug > Exceptions) or the keyboard shortcut (Ctrl+D followed by E).
Under the Thrown column, check the box next to the Common Language Runtime Exceptions. Now, when an exception is thrown, VS immediately breaks at the offending line of code. One word of warning - Depending on the size of the code, you may find far more exceptions being thrown than you had expected.
Say you have code that looks like this:
try
{
int a = 2;
int b = 0;
int c = a / b; // does not compute
}
catch
{
// Do nothing
}
If you were to run this code, you'd never know that there was a problem (well, not until you tried to use the value of 'c' elsewhere.) What we want to do is have the debugger break as soon as an exception is thrown. To do this, open the Exceptions dialog, either through the menu (Debug > Exceptions) or the keyboard shortcut (Ctrl+D followed by E).
Under the Thrown column, check the box next to the Common Language Runtime Exceptions. Now, when an exception is thrown, VS immediately breaks at the offending line of code. One word of warning - Depending on the size of the code, you may find far more exceptions being thrown than you had expected.
Subscribe to:
Posts (Atom)