There are as many ways to code a program as there are programmers—and Visual Basic is no exception. But while sewing together a patchwork of designs and styles may work for a quilt, that’s not a viable approach when it comes to enterprise application development. Consistency is essential in developing maintainable, bug-free applications. One way to ensure consistency is to establish programming standards that your team, or indeed the entire enterprise, can follow.
C and Java programmers have traditionally regarded VB as a simple language. But as VB becomes more powerful and object oriented, it’s becoming increasingly important for VB developers to follow some guidelines to keep their code easy to read and maintain.
Why do you need to use VB coding standards?
To understand why coding standards are important, consider these possible scenarios:
- You develop a module and go on vacation; meanwhile, another team member is struggling to understand your code and to modify it as per new requirements.
- One of your team members develops several code modules—and then leaves the company. You’re asked to quickly make a large number of modifications, only to discover that you’ve landed in VB hell.
- You’re asked to modify a piece of code you wrote five months ago, but when you open it up, it takes you more than an hour to figure out what you wrote and why.
All of these situations could represent the start of a very bad day for you and your coworkers. To avoid such problems and to improve the quality of code, team leaders and project managers should establish coding standards for their groups and ask all developers to follow them.
According to Microsoft, you should use coding conventions to "standardize the structure and coding style of an application so that you and others can easily read and understand the code." Microsoft offers the following guidelines.
All objects and variables should be named with a consistent prefix that identifies the type of object. See Table A for suggestions.
|Suggested prefixes for frequently used controls|
For other controls, use a unique two- or three-character prefix. To distinguish third-party controls from intrinsic controls, you can include a lowercase abbreviation for the manufacturer.
Use consistent naming conventions for variables and constants. Always declare variables with the least scope possible. Most of the time, declaring global variables is not a good idea because you can pass module-level variables with property procedures. Your module may be using a global variable, but someone else's module could modify that variable's value without your knowledge, so you end up with a bug in your code. Use global variables only when you don't have any other convenient way of sharing the data between forms/modules. When you have to use global variables, declare all of them in a single module and give a module a meaningful name, such as Public.bas.
Variables should have the following scope:
Declaration: Private in procedure, sub, or function
Visible in: The procedure in which it is declared
Declaration: Private in declarations section of a form or code module
Visible in: Every procedure in the form or code module
Declaration: Public in the declarations section of a code module
Visible in: Everywhere in the application
In general, it's advisable to write modular code whenever possible. This helps organize the application's code and makes it possible to reuse components, make changes to a specific function, and fix bugs.
It is also a good practice for your procedures and functions to operate only on objects passed to them. Global variables that are used in procedures should be declared at the beginning of the procedure. In addition, you should pass the arguments to procedures and functions ByVal, unless you need to change the value of a passed argument.
As a naming convention for variables, use these one- and two-letter prefixes for each corresponding scope:
- Static: st
- Module-level: m
- Global: g
Here are some things to keep in mind when working with variables:
- Declare all variables before using them. Always turn on Option Explicit to force Visual Basic to check that each variable is declared before it can be used. For example, if Option Explicit is off, you can use variable intARP without declaring it first. But if you mistype it as inrAPR later in your code, VB will create variable inrAPR and assign 0 to it, so your application will use 0 instead of the value of intARP.
- Choose data types carefully.
- Use the variant data type only when necessary (i.e., when no other data type can be used).
- Make the data type of the variable obvious by using an appropriate prefix. When declaring variables, always specify the data type for each one you declare. If you declare Dim strSQL, strPassword as String, only variable strPassword is a String variable; strSQL becomes a Variant variable. Be sure to specify the data type for each variable you declare.
- Make the purpose of the variable obvious by using a descriptive name. (How many times have you seen variable String1 and had to figure out what it stands for?)
- Make variable names as long as necessary to describe their purpose.
- Use minimal variable scope.
- Use mixed case in the names (strNameFirst is easier to read then STRNAMEFIRST or strnamefirst).
- Use Hungarian notation (three-character prefix) for naming your variables (see Table B).
|Suggested data type prefixes for variables|
Constants and enumerations
Constants and enumerations can improve your code in several ways. Here are a few of the benefits:
- They make code easier to read by giving a name to the value (e.g., TaxRrate instead of .05).
- They make it easier to modify values because there’s only one occurrence of the actual value, and it’s generally located in a “central” location, like the top of the program.
- They reduce errors caused by transposing or mistyping numbers.
You should always use constants instead of simply hard-coding values (using "magic numbers") because constants are easy to modify. Name your constants in a way that makes their purpose and scope obvious. All constants should have a prefix specifying the scope, as well as letter c followed by an underscore—for example, mc_HoursPerDay for module scope constant and gc_HoursPerDay for global scope constant.
Using enumerations results in a cleaner code. Instead of having to remember all possible numeric values of a parameter, you can assign names to numeric values and then simply use the names. Always use descriptive names that are easy to understand for your enumerations.
Procedures and functions
Procedures and functions should be specific and self-contained. If you are accessing a database and putting the data returned into the grid, don't do both in the same procedure. Separate data access from displaying data in separate procedures.
Having specific and self-contained procedures helps debugging the code. It also promotes code reusability. If your procedures depend on other procedures, debugging the code becomes more complicated, and code reuse is harder to achieve.
Try to minimize usage of global and module-level variables (which make procedures dependent on the data outside of their complete control); pass arguments to procedures and functions instead, whenever possible. By doing this, you clearly define the interface between the module and the calling routine. Passing data in or out of a module any other way bypasses this interface, which can make debugging and future enhancement complicated.
But what do you do when you can’t pass data through use of parameters? Document, document, document. Copious comments in the called module, as well as everywhere else the global variable occurs, will pay dividends long after you you’ve forgotten about the project.
It is a good idea to structure your procedures and functions in a way where they don't call many other procedures and functions. This creates self-contained procedures and functions and reduces dependencies.
Procedures that have only one exit point are easier to debug. You can always be sure your cleanup code will be executed before the exit. To achieve this, create a label at the end of every procedure and always give it the same name, like Exit_Proc. Under it, put all the necessary cleanup code for each procedure together with the Exit statement (Exit Function or Exit Sub). In the procedure code, whenever you need to exit the procedure, just call Exit_Proc (GoTo ExitProc) instead of calling Exit Function or Exit Sub. As in the case of using parameters, having only one entry point and one exit point serves to define the interface for the module.
Module and procedure design
Here are some things to consider when planning your code and some ways to modularize your application:
- Always give procedures and modules descriptive names.
- Make procedure names as long as necessary to describe their purpose. If you are creating a function to return tax on a purchase, call it CalculateTax; don't call it Calculate, because it's hard to tell what your function is calculating.
- Always specify the scope of procedures. Use Public Sub GetData() instead of Sub GetData().
- Create specialized procedures/functions.
- Make procedures/functions as self-contained as possible.
- Always use mixed-case declarations.
- Give every procedure a single exit point.
- Call procedures/functions in a consistent way. Either always use a Call statement or don't use it at all.
Time well spent
The suggestions presented here may not be exactly how you would like to do things. Fine. The important thing is consistency. Develop a standard that your team will follow. If you don’t already have a set of standards in place, however, you may find that these suggestions can go a long way toward improving your team’s productivity.
Following these standards may also take a little more time than you are used to. But because investing a little extra effort now is significantly more efficient than having to deal with debugging and maintenance problems later, I am confident that you will find that it’s time well spent.