Coding guidelines for Visual Basic 5/6 and VBA for Office

A collection of recommendations, conventions and some scaffolding, for naming and crafting solutions in classic (VB5/6) and Visual Basic for Applications, that the code on this website, francescofoti.com, as the code in the applications I develop and commercialize thru my company devinfo.net, adheres to.

Foreword

Please note that these coding guidelines in no way pretend to be canon, they’re just my personal preferences and they reflect the coding style that has grown to be mine.

I wrote the first version of these guidelines back in 2014, because I needed to document and share my code with coworkers (I so always liked to document my work). As I’m going to share more of my codebase in the coming year(s – hopefully), I’m now (end of 2019) publishing this more complete version, to smooth your dive into the code you may encounter around here, and possibly get the most out of it.

I only added to these guidelines since I first defined them, and I still stick to them today.

I know every one of them is debatable and every developer has his or her own style and convictions.

However, these guidelines, as they are, and even since before I put them on paper, faithfully helped me acquire and maintain consistency and a high level of code maintainability throughout the years. I hope they can at least encourage you to build or step your own guidelines up, if you’ve not already done so.

Naming conventions

Hungarian notation

The”Hungarian notation” principle basically consists in using a set of standard prefixes (all in lower case), before each variable name to indicate its scope and type. Use it.

Exceptions to this rule, or when it may not be appropriate to use the Hungarian notation to name things, are indicated along, and there’s a paragraph about those exceptions later.

Constants

  • Do not use Hungarian for constants (and there’s your first exception).
  • All constant names are in upper case and, if they consist of multiple “words”, they may use an underscore to separate them.

Examples:

All constants should declared with their type (As …)

Examples:

Note the ampersand (“&”) after the “3”, that matches the type of the constant (here a Long).

Variables

As long as we’re talking about where a variable is defined inside a project, scope in Visual Basic is simple. A variable can be global to the entire application, or local to a class module or standard module (or any other document provided by the development environment like a Form or a Sheet), or local to a function (or Sub).

Prefix Scope
g Application global scope
m Class, module or other document (Form, Report, Sheet, etc…). “m” stands for “Member” variable.
(none) Local to the current function (or Sub), or see [Exceptions]

There is no underscore following this 1 letter prefix.

Visual Basic native data types prefixes

Visual Basic data type Prefix
Integer “i”
Long “l” (minus L). Although in rare cases I don’t mind loosely using “i” when confusion wouldn’t matter so much.
Variant “v”
String “s” (and not that horrible “str”)
Boolean “f” (stands for “flag”, while I use “b” for the Byte data type)
Byte “b”
Single “sng”
Double “dbl”
Currency “c” or “cur”
Object “o” (and not “obj”)
Date “dt” (I use the Date datatype in very rare occurrences, I mostly use Variants for dates)
Collection “col”

Local variables used as loop iterators (i, j, k, …)

There’s nothing wrong in using one letter variable names for Integer or Long indices (or indexes), whether it is in loops (For, Do, While, Repeat, …) or for index access to an item. But it may help using longer indices names (like iCustomer, iLine, iCol) when it helps disambiguate heavily packed code. For iteration on other data types (object reference, interface, structures, etc…), avoid one letter iterator variable names.

#personal: Use “i” for rows, and “j” for columns, then “k” for anything else. If nesting more, think of using more explicit variable names.

UI elements classes

UI element Prefix
Form “frm”
Report “rpt”
Form used as a (modal or modeless) dialog “frm” + name + “Dialog”, (eg. frmLoginDialog)
Subform (Access) “sfrm”
Class module as an interface “I”
Textbox “txt”
Combobox “cbo”
Listbox “lst”
Button “cmd”
Label “lbl”
Tab control “tab”
Checkbox “chk”
Option button “opt”
Scrollbar “hsb” or “vsb” (horizontal, respectively vertical)

Other datatypes

Data type Prefix
Type definition (structures) “T”
Object of any other class “o” or a 2 or 3 letter prefix for the class, like “cn” for and ADODB connection, or “rs” for a Recordset, etc…
Standard module “M”
Class module “C”. This is unless you expose by any means the class outside of your project or database, in which case you should use a non prefixed class name (also see [Exceptions]).
Class module, used as an interface “I” (uppercase “i”)
Defined Type “t” or “tag”

Inside a type definition, use Hungarian notation for member variables, example:

Function parameters

Every function / sub parameter variable is prefixed with a “p”. That’s another instance of an invaluable indicator in the body of the function’s code that allows to quickly identify if a variable is a parameter that has been passed to the function, among the ones that have been declared in its body. Without this prefix, it may be sometimes very difficult to distinguish between the origin of variables while reading the function’s code.

#personal: You’ll also quite commonly see the prefix “Ret” inserted after the variable scope and type prefix, when a function parameter variable (ByRef) is going to be modified in a meaningful way for the caller, by the function.
I also insert “New” after “Ret” if the (ByRef) parameter variable is an object that will be created inside the function.

Arrays

Every array variable is prefixed with an “a”, after the scope letter prefix, examples:

Enumerations

Enumeration definitions are prefixed with a minus “e”, as are the enumeration value names. Try to choose value names that can tip enough about what type of enumeration the values refer to, to mitigate confusion. In the sample below, the “eLeft”, “eCenter” and “eRight” symbolic names, may still be slightly ambiguous; this is acceptable if there is no near possibility of ambiguity.

Examples:

Functions names

Let’s assume that when we refer to functions, we’re also talking about procedures (sub).

  • Use Pascal case convention, where each word in the function name is capitalized.
    No Camel case (first letter of the function in lower case, the other uppercase).
  • Never separate words with an underscore. This is because underscores are automatically added by Visual Basic in event procedures (and interface implementations), so they would only add unnecessary confusion.

Name of functions in standard modules

API and module mnemonic

When crafting a standard module to handle a specific set of tasks as an API, prefix all the public functions in the module with more or less one to five uppercase characters that qualify the module where they belong: the module mnemonic.

Don’t prefix the private functions.

Example:

Module name is: MADOAPI (which I prefer over MAdoApi as ADO and API are rarely seen in lower case).
The chosen module mnemonic is “ADO”, it will prefix each module member function.

Sample of some of the functions signatures, prefixed by “ADO”:

Collection of related functions

If the module is just a collection of helper functions that are logically grouped together in the module, don’t prefix the module’s member function with a module mnemonic.

Example:

Module name is: MStrings (Not an API, a collection of functions helping with string manipulations). No Prefix:

(You can find articles explaining both these functions on my personal website)

Exceptions

Do not use Hungarian notation for variable names, in these two cases:

  1. Variables used as Public properties in a class module:
    This happens when a variable, declared as Public, in a class module, is not wrapped with get/let/set property accessors, but is directly exposed (non managed R/W/Set).
    Sample, in which we see a part of the declaration section of a class module:

  1. Global objects:

Error management

Recommendations

  • Return only basic native types, or objects references from functions (and property procedures).
  • Do not return arrays, pass them by reference.
  • Return True to indicate success, False to indicate failure. In case of failure, use the following guidelines to provide more contextual information.
  • Avoid using error handlers to implement behavior as much as possible. Always jump from an error handler to an exit label in the same function, with VB resume label statement, don’t let the error handler be an exit point of your function, or jump conditionally at different points in the function.
  • Do not raise errors, do not use Visual Basic Err.Raise method, instead trap errors in your error handler, return False from the function that trapped the error and set the module or class “error context” (see SetErr() function below).

In standard modules

This applies if the module represents an API (not for collection type modules). According to these guidelines, you should already have your public functions prefixed with the module mnemonic.

Add the following private member variables, in the declarations section of the module:

Add the following functions, prefixing them with your module mnemonic. As an example, they’re here prefixed with “ADO”:

In class modules

Add the following code:

Trapping and handling errors

Once you’ve added the previous code, you can handle trappable errors in your functions, like in this example:

Then

Thank you for reading or skimming these guidelines. Now that the boring stuff is behind us, I’ll try to bring more fun stuff in the next posts.

Please rate and comment here freely or tweet me @unraveledbytes, I’ll be happy to read you.