Release checklist for programs

This article presents a code review checklist to go through before releasing a program to users.

High software quality requires extensive testing. Unfortunately, no amount of testing can reveal all errors. Code review can find problems that testing can't. The code review can be done manually or with automated code audit software.

This article presents a short checklist to review before releasing a new version of a program. It's a quick list that doesn't require extensive effort to go through, so that the checks can be performed before each minor release. More extensive code review should be performed from time to time to optimize the program further, to locate hidden bugs and to reduce maintenance efforts.

The checklist focuses at Windows programs written in Visual Basic, but you can use it with many other programming languages and environments as well.

Project Analyzer is a VB code review tool that can be used as an automated checklist verification tool. It supports all the review rules mentioned in this article.

Remove unused modules

Many programs contain parts that are not in use. These unused parts are called dead code. The amount of dead code in a "dirty" program can well be something like 30-40%. Complete dead code removal is a demanding task. Ideally, it should be done at regular intervals. Fortunately, not all dead code is equally bad. You can remove the more problematic parts and decide to keep an acceptable level of dead code.

At a minimum, remove the following types of dead code:

Unused files, such as dead classes and dead modules. They add extra baggage to the program. They contain untested code and hidden bugs that can accidentally come back to life during maintenance. Exclude unused files from the program. Mark them untested if you intend to use them later in order to prevent accidental reuse of possibly erroneous code.

Used but uninstantiated classes. These classes are a source of nasty crashes. They are a type of semi-dead code. This case consists of classes which are used as data types (in As clauses), but which are not instantiated at run-time (with As New clauses). When the code calls such a class via an object variable or a pointer, the program can crash due to Object variable not set or NULL pointer. Determine whether you should instantiate these classes or remove them altogether.

Unimplemented interfaces. Defining extra interfaces just adds baggage to the program. Remove them. If someone calls the interface later on, the code will fail as there is no object to respond to the calls.

Implemented interfaces that are not used. An unused implementation adds weight to the executable but no benefit. Remove it. The implementation was probably not properly tested anyway. Bringing it back to life without testing it is likely to cause trouble later during maintenance.

Remove other unused things

Events that are not raised. An event that does not fire must be a bug or a leftover from a removed feature. Raise the event properly or remove it.
The event will cause headache later in maintenance when someone expects it to fire.

Invisible controls on forms. A control may be invisible because someone flagged it so (Visibility=False), or because someone dragged it off the form borders. In either case, the control is a potential source of errors. It may belong to an unimplemented feature or a feature that should have been removed.
The code that sets the properties of the control and handles its events is unnecessary as well. When you delete the control, you get a chance to delete that code too.

Disabled controls. A control may have been disabled for some reason (by setting Enabled=False). It shows up in the user interface but it's never enabled, so it serves no purpose. You should remove it as it distracts the users. Again, you get a chance to delete the code that manages this useless control.

Review empty procedures and modules

Sometimes you find an empty procedure or an empty module. That's odd. Here is what happened:

You forgot to write the code! Write it now. OR

You deleted all the code there was. You kept the procedure (or module), because you didn't feel like removing it completely. Now it's time to delete it for good.

Fix functionality problems

Program ends: Stop and End statements. Verify that each end is intended and not just a leftover from debugging sessions. A premature end can lose or corrupt data and cause severe user aggravation.

Events not handled for WithEvents variable. The events of an object variable are not handled, even if the variable was marked WithEvents. Some important feature may be missing from the program. Handle the missing event(s). Alternatively, remove the WithEvents keyword to make your program run faster.

Procedure not found in library. When you declare and use functions from external DLLs, make sure the functions actually exist there. There may be different versions of the same DLL. A function your program uses might not exist in an old DLL version. This check is especially important when you plan to release code that you received from someone else. Your version of the DLL might be different from that of the original developer. Check your code against the DLL versions your users might have, not just the version you have on your own disk.

Fix user interface anomalies

Now we move on to polishing the windows and dialogs. This is also an important part as it affects what the users think about your application. If the UI is not performing the way they expect, that will affect the entire user experience. Giving the UI a final polish might not be intellectually challenging for the developers, but forgetting to do it will be challenging for the users!

Form missing Icon. When there is no icon for a form, the program will display a default icon. This is an easy way to give a program an unfinished look.

Form missing HelpContextID. A program with a context-sensitive help file is supposed to set a HelpContextID for each form. When HelpContextID is missing (it is set to 0), the correct help page will not be shown when the user presses F1. Setting the context ID numbers is an easy way to reduce the number of help calls from users (assuming the help file itself is well-written, of course).

Dialog missing Default button. Each dialog box should have a Default button (titled OK, Save, etc.) so that the user can press the Enter key to accept the dialog. A missing Default button means the user cannot use this interface standard.

Dialog missing Cancel button. Each dialog box should have a Cancel button (titled Cancel, Close etc.) so that the user can press the Esc key to dismiss the window. This is the way users expect all programs to work. If they can't press Esc, they will feel trapped in that dialog. You need to have good reasons to disable Esc in a dialog. Naturally, main forms and floating toolboxes don't require this setting.

Hotkey conflicts and missing hotkeys. Each control and menu item should define an access key so that users can press an Alt+key combination to access the control. A missing hotkey means there is no Alt+key combination for that control. A hotkey conflict means that two or more controls use the same Alt+key combination, which is most likely an error. A hotkey problem makes it harder to use your program with the keyboard. A missing hotkey can even prevent use without a mouse. Everyone has a mouse these days, right? That may be correct usually, but don't forget that the keyboard is often the fastest way access a program. Add hotkeys to serve the power users.

Resizable form missing Form_Resize. A form that does not handle the Resize event will look really odd when the user resizes it. That's a when, not an if. Someone will resize it even if you didn't think so. Add resizing code or prevent resizing by changing the form's BorderStyle property.

Error event missing. A Data control that does not handle its Error event can cause your program to crash on errors and even corrupt the data.

Click event missing. A button or menu item that does not handle its Click event does nothing when clicked. It's like a dead button or dead menu and causes users to call you for help.

Timer event missing. A timer does not fire any activity. The program seems to be missing a feature that was planned earlier. Alternatively, the timer has been forgotten there and it's just consuming resources while doing no good.

Timer interval below 55 ms. This VB-specific rule is there because VB can't fire a timer any faster than every 55 milliseconds. If you need finer timing, use some other way.

Twisted tab order. As the user presses the Tab key, the focus should move to the next control in a predictable way. Usually this is from top-left to bottom-right (top-right to bottom-left in Hebrew and Arabic programs). Deviations from the normal tab order make your program awkward to use. The focus jumps in a seemingly random pattern and users could even make nasty errors with your program.

Note: Project Analyzer detects this group of UI problems for VB6 forms, but not for VB.NET or VBA.

Find hidden VB errors

Visual Basic allows one to use undeclared variables. They should never be used in production code, as they are a potential source of errors. Set Option Explicit on for each module to require variable declaration. This is a great way to spot typing errors and also to define proper data types for variables.

Never use a fixed file number for file access (such as in Open filename For Input As #1). If the file is accidentally left open (due to an error, perhaps), subsequent file access using the same number will fail. This may not show up during testing, even if it affects the users from time to time.

Optimize compile flags

Now that your program seems to be running fine, let's optimize a little. We're not going into detailed optimization here, just a few compiler settings to verify:

Check the compilation optimization flags. Release code should be optimized during compilation, either for speed or for size.

Verify the base address. Always define a base address (other than the default one) for your libraries. It helps your library to load faster. It doesn't really matter what the base address is, it just needs to be different from the other libraries. Since all the other libraries use the default setting anyway, you can be smarter and define your own.

Release it!

OK, now you can compile and release your program! Good luck!

Project Analyzer is a VB code review tool that can be used as an automated checklist verification tool. It supports all the review rules mentioned in this article.