We already talked about how to make your forms and views easier to maintain. But in most Notes/Domino applications, a majority of the code is written in Lotusscript (at least for traditional applications, XPages are outside the scope of this discussion for now).
It is not hard to write easy to read Lotusscript code. Just follow some simple rules. As always, there are more than one way to do things, but this is how I do it (most of the time). I found that this works for me.
- Always use Option Declare (or Option Explicit) in all your code. This will prevent you from getting errors later when (not if!) you spell a variable wrong. Option Declare forces you to declare all variables before you use them. It also helps you by warning if a function or method is called with a variable of the wrong data type. Most of your bugs can be avoided using Option Declare. Use it!
- Give your variables meaningful names. There is no need to abbreviate variables and/or give them cryptical names, or you will just spell them wrong later. Use a naming system that makes sense to you. As I mentioned in my previous post, I avoid Hungarian notation, it just makes the variables hard to read. Since Lotusscript is a strongly typed language, the compiler is taking care of data types. I however do use some indicators for certain variables. For the few global variables I use, I do prefix them with g_, as I then can give local variables a similar name. By giving your variables meaningful names, the code also becomes largely self-documenting.
- Be consistent in how you name related variables. If you have a variable named startpos to indicate the beginning position of a string within another string, name the other variable endpos, not posend. Think through all variables you need so you give them good names. The variables posstart and posend are not as easy to read and understand as startpos and endpos. Of course, using CamelCase, posStart and posEnd are much easier to decypher.
- Use the same/similar naming convention for the Domino objects as IBM uses in the online help. Use ws, uidoc, session, db, doc, etc. If you need more than one object of any type, e.g. NotesDatabase, name them in a consistent way. I always use thisdb for the database the code is executing in, nabdb for names.nsf, archivedb for (you guessed it!) an archive database, etc.
- Declare the variables at the beginning of the code/subroutine/function. Don’t declare them throughout the code, this makes it much harder to find them later, when you perhaps need to see what data type they are.
- Declare the variables in a logical order. I always group the variables by type. First the UI classes, then backend classes, then any custom classes/data types, and finally other variables. Within each group, I declare them in the order I am using them. The benefit of this is that you get an idea of where to look for the variables in your function/subroutine.
- Use Option Declare. Yes, this needs to be repeated. Way too many (especially new) programmers forget this, or don’t know about this.
Here is a typical example of how I would declare variables in one of my applications:
Dim ws As New NotesUIWorkspace Dim uidoc As NotesUIDocument Dim session As New NotesSession Dim thisdb As NotesDatabase Dim nabdb As NotesDatabase Dim nabview As NotesView Dim persondoc As NotesDocument Dim doc As NotesDocument Dim username As String Dim phonenumber As String Dim employeenumber As Integer
As you can see, this function will do some kind of lookup in names.nsf and retrieve the name, phone number and employee number from the person document. It is then (probably) storing the values into the backend document of the currently open document. Just by declaring and naming variables in a logical way you can often figure out what the code will do further down.
If you declare variables that you end up not using, remove them. Since you now are a good programmer who is using Option Declare, you can very easily test if a variable is used by commenting it out. If you don’t get an error, you can remove it.
This brings us to the art of commenting. Yes, it is a little bit of an art, and I have seen way too much code written where the code is either overly commented (not very common), or hardly at all (very common). The art is to add just the right amount of comments.
There is an old saying among programmers: “if the code was hard to write, it should be hard to read”. But it may not be another programmer that have to look at the code and find a bug in it or update it with new functionality, it may very well be you. And if you have commented your code, you will not have to analyze every line of code, trying to figure out hwo you were thinking a few months or even years ago.
You want the comments to explay
“why”, not “what” the goal of the code, not explain the individual lines of code. What I mean with that is that it does not make sense to have code like this:
'*** Set key variable to current user's name key = session.CommonUsername '*** Get the "People" view in database Set view = db.GetView("People") '*** Get person document in view based on key Set doc = view.GetDocumentByKey(key) '*** Get the email address from the field "InternetAddress" emailaddress = doc.GetItemValue("InternetAddress")
The comments in the example above does not add anything to the code. This code below is much better, and also shorter:
'*** Get email address for current user from person document key = session.CommonUsername Set view = db.GetView("People") Set doc = view.GetDocumentByKey(key) emailaddress = doc.GetItemValue("InternetAddress")
This comment explains what we actually are attempting to do. In real life, you would probably break out this code into a separate function with a descriptive name. The example above is just for illustrative purposes. I will cover functions in a future article.
Comments are crucial when you use some kind of work-around, or when your variables or field names have been named something that is not clear. I have cases where a field was initially named for the data it would hold, but later it was changed to something totally different. Make a note of that in your code. Or if you have to use some more or less obscure work-around to get the code to work, it would be very annoying if you (or another programmer) later removes that line. A typical example is to perform a db.FTSearch() with a query that will not return anything to get an empty document collection for later use.
I use the following system for comments:
- Comments explaining one line of code or a command are put on the same line, with no stars, just an apostrophe.
- Comments explaining a section of code are put on their own line, and I start them with three stars to make them more visible and make it clear they are comments and not code.
Remember, comments are there to help you remember (or tell someone else) what is happening in the code.
Bringing it together
Let’s look at a real life example. This one is taken from one of the IBM developerWorks forums, where a user had some issues attaching files to a rich text field. After getting some help, he posted the code he finally got to work:
Sub Initialize Dim s As New NotesSession Dim ws As New NotesUIWorkspace Dim uidoc As NotesUIDocument Dim filesys As String Dim FullPath As String Dim OUName As String Dim FileName As String Dim Path As string Dim db As NotesDatabase Dim view As NotesView Dim doc As NotesDocument Dim Object As NotesEmbeddedObject Dim IDFile As string Set uidoc = ws.CurrentDocument OUName = uidoc.FieldGetText( "OUName" ) FileName = uidoc.FieldGetText( "FileName" ) IDFile = uidoc.FieldGetText( "IDFile" ) Set doc = uidoc.document Call uidoc.Save Dim FullPath1 As Variant FullPath = doc.GetItemValue("FullPath")(0) Dim rtitem As New NotesRichTextItem (doc, "FileName") Set Object = rtitem.EmbedObject(EMBED_ATTACHMENT,"",IDFile,OUName) Call doc.Save(True,True) End Sub
As you can see, variables are declared all over the place, some declared but never used, inconsistent named, and of course no comments.
Here is my cleaned up version of the same code, with comments added as well:
Sub Initialize Dim ws As New NotesUIWorkspace Dim uidoc As NotesUIDocument Dim doc As NotesDocument Dim rtitem As NotesRichTextItem Dim filepath As String Dim filename As String '*** Save currenly open document Set uidoc = ws.CurrentDocument Call uidoc.Save ' Save UI doc so backend doc get updated '*** Get updated backend document and read field values Set doc = uidoc.document filepath = doc.GetItemValue("FullPath")(0) ' Path must end with / filename = doc.GetItemValue("FileName")(0) '*** Create a new rich text field called 'Attachment' and save doc '*** Use GetFirstItem() if the field already exists in document Set rtitem = New NotesRichTextItem(doc, "Attachment") Call rtitem.EmbedObject(EMBED_ATTACHMENT,"", filepath + filename) Call doc.Save(True,True) End Sub
I think this code is much easier to follow, cleaner and easier to maintain. This is the kind of code I would like to see if I came in to update or modify an existing Notes application. Wouldn’t you?
Update: Made some small changes and clarifications, and fixed some typos.