My five VBScript 'best practices'

I've been programming in VBScript for a while now. Over that time I've seen some things that for better or worse made me take note. I thought that I would share those, in an attempt to at worst open a dialogue on some level and at best convince some VBScripters to slightly modify their actions.

1. Misuse of On Error Resume Next

The On Error Resume Next statement can be quite useful, but all too often it finds its way to the top of a VBScript. Doing this will basically render the entire script free of errors. The statement does have its uses though. Lets say that I want to open a file for writing. Lets consider the things that could go wrong

  • No Permission to write there
  • File exists already
  • File is locked
  • Disk is full
  • User doesn't have enough disk quota space
  • Saving to Network Drive that is offline
We could spend half a day and code around each one of these items. Instead we could do the following:

On Error Resume Next

If Err Then
End If
On Error Goto 0

What happens here is that everything between the On Error Resume Next and the On Error Goto 0 statement don't raise errors. Everything before and after will still cause issues. We can test for errors by checking the Err variable. It returns true for non-zero numbers. So if we have an error code we enter the condition and handle the error. Once done handling our error we call Err.Clear. This clears out the error code so when we test it later in the code we don't get this error. The last statement simply turns error suppression back off.

Also, if you call On Error Resume Next in a function or subroutine then it will only stay in effect for that function and all calls made by that function.

I prefer the second option, just because I like knowing where error suppression starts and stops. With the second option, you can turn on error suppression in one function, call a second function and still have error suppression turned on.

My main point in all of this, is that you have to know where your script is failing. Covering up the problems will curse you sooner or later. Its ok to use error suppression, but so long as you are handling the problems when they arise.

2. Not using Option Explicit

I'd almost like to say as a general rule, you should take both this and the previous rule together and for every script that has On Error Resume Next as the first line of code, should be replaced with Option Explicit. In fact most every script should have Option Explicit as the first line.

What Option Explicit will do for you, is make sure your variables are declared. It does this, by not allowing undeclared variables in your code. So if I said

Option Explicit

i = 0

I'd get an error of "Variable is undefined 'i'"

I'd have to add

Option Explicit

dim i
i = 0

You can add multiple variables by separating them by commas

dim LastName, FirstName, BirthDate

All this will save you the hassle of debugging your code only to find out that you mistyped your variable name.

This may seem like a lot of work to do. Ultimately though, it is only really 2 lines of code. Once to turn this option on and a second to declare your variables. What you gain is completely eliminating one of the more common reasons why scripts fail.

3. Don't repeat yourself

I've seen a lot of scripts that are three or even four times longer than they need to be. This happens because they are simply copying and pasting their code and only modifying one or two parts. Whenever you find yourself in this situation, you should immediately start thinking of sub routines. Sometimes you may not notice until after you've written your script. In those cases, if you ever think that you'll need to modify this script you should go back and refactor it correctly.

Here is an example of what we can refactor.

ItemToWrite = Items(ItemOne)
WshShell.RegWrite "HKLM\Software\MyProduct\ItemOne", ItemToWrite

ItemToWrite = Items(ItemTwo)
WshShell.RegWrite "HKLM\Software\MyProduct\ItemTwo", ItemToWrite

ItemToWrite = Items(ItemThree)
WshShell.RegWrite "HKLM\Software\MyProduct\ItemThree", ItemToWrite

This gets refactored into

WriteKey "HKLM\Software\MyProduct\ItemOne",Items(ItemOne)
WriteKey "HKLM\Software\MyProduct\ItemTwo",Items(ItemTwo)
WriteKey "HKLM\Software\MyProduct\ItemThree",Items(ItemThree)

Sub WriteKey(KeyPath,ItemToWrite)
WshShell.Regwrite KeyPath,ItemToWrite
End Sub

Now we have one line per item instead of two and a single point where we are writing to the registry.

But, lets go a bit further. In this code we repeat "HKLM\Software\MyProduct\" three times. What happens if we misspelled one of those lines. Worse is if we need to change it. We now have to change three lines. Lets change this to

Const MYKEY = "HKLM\Software\MyProduct\"
WriteKey MYKEY & "ItemOne",Items(ItemOne)
WriteKey MYKEY & "ItemTwo",Items(ItemTwo)
WriteKey MYKEY & "ItemThree",Items(ItemThree)

Sub WriteKey(KeyPath,ItemToWrite)
WshShell.Regwrite KeyPath,ItemToWrite
End Sub

We did add a line, but in turn we saved our self the trouble of changing three lines whenever we change that key path.

Whenever you intend to use a string or a number several times, it is best to simply put it in a constant and simply reference it. If you need to modify the string once and then use it multiple times, I'd recommend just using a variable instead of a constant. Either way it will help you when modifying your code.

4. Not using descriptive variable names

In some of my examples, you may see me using a single letter for a variable (Such as i). In practice though this is only good in a few specific examples. Many scripts that are giving examples of some technique will often use a non-descriptive variable(s). I think this leads to individuals using non-descriptive variables in their actual code. Your script will determine your level of descriptiveness, but sometimes just saying 'Name' can be good. Othertimes you'll need "LastName'. Either is better than 'n'.

I deviate from this in one distinct way. When using a loop that has a variable to be used as the index of an array I'll likely use just 'i'. This is fairly common. Sometimes nested for loops for will i and then j. At this point you may be better off using 'Row' and 'Col'. Again, that all depends on your situation. In those situations though, you are using a variable in a single location and not interspersed throughout your code.

5. Not using Comments

I look at programming/scripting languages as a compromise in communicating your actions with the computer and with other users. Comments allow us to extend our communication with the user. By user, I don't just mean you. I also mean the next person who gets the 'joy' of reading your code.

A lot of people do believe in writing comments, but still don't get it right. A comment should be used to say why you did something, not what you did. Most mediocre scripters will be able to take your script and decipher what you did. What they won't be able to do, is figure out why you wrote the code you did.

Taking this approach can sometimes mean writing less comments than you normally would. Jeff Atwood expanded on the idea of commenting on why you did something and had some excellent suggestions on what you can do to be more descriptive with your intent.

No comments: