Self-documenting Source Code
In this article, we explore ways to make your Visual Basic source code
self-documenting and thereby make it easier to read, understand and maintain.
Although we will be referring to Visual Basic, it is worth noting that the
techniques discussed in this article are equally applicable to most other
programming languages too.
Keep It Simple
However over used you believe the phrase "Keep It Simple" to be, it is still
ignored too often during software development. To make your source code self-documenting you must be prepared
to take this phrase to heart. Simplicity itself aids understanding and therefore
by definition makes your code easier to test and maintain.
Make simplicity and ease of understanding a deliberate goal. Avoid "clever"
code whose intent is not obvious or complex code which requires detailed comments. Under
such circumstances it is often better to simplify the code than attempt to explain it.
Let The Compiler Do The Work
Modern optimising compilers take much of the pain-staking effort out of memory
management and performance tuning. The extra lines of code you might employ to ease
understanding are therefore likely to have little additional impact on memory usage
or performance.
Commenting Code Effectively
Comments can add considerable value to the achievement of self-documenting source code.
To be relevant, comments must be located close to the code with which they
are associated. The best comments summarise the purpose of a piece of code or explain the
intent of the code which follows, leading the reader into what is about to happen next.
The emphasis placed on comments therefore is to document "what" or "why"
rather than "how". The code itself describes "how".
Things to do when commenting code:
Make each comment count - be concise and precise.
Describe the purpose of each source file.
Describe the purpose of each procedure.
Include the name of the author of each source file and procedure.
Describe the inputs and outputs of each procedure.
Explain assumptions made by code such as execution sequence, valid values or range limits.
Include references to formal documents such as test plans and specifications.
Explain work-arounds such as code that avoids an error or improves performance.
Explain undocumented features.
Explain global/module-level declarations.
Explain units of data declarations (e.g. hours, minutes, seconds etc.).
Explain enum element value meanings (at bit level if necessary).
Explain global variable declaration and usage.
Explain use of abbreviations (preferably avoid them).
Remove redundant comments.
Remove commented-out dead code.
Correct erroneous comments.
Things to avoid when commenting code:
Avoid comments which echo the code in different words.
Avoid use of abbreviations within comments whenever possible.
Avoid commenting styles that are time-consuming to maintain or difficult to amend.
Avoid inaccurate comments as these serve only to mislead the reader.
Avoid self-indulgent comments.
Avoid cryptic comments.
Avoid end-line comments which can become cryptic and are difficult to format.
Avoid verbose and superfluous comments.
Avoid littering code with vast amounts of comments for version history information.
Avoid commenting-out redundant code - delete it.
Don't document bad code - re-write it.
Reminder comments (distinguished by a common starting prefix such as 'TODO:) can
offer valuable advice to the reader regarding work which is still to be done or currently
in progress. Such comments should include the date, author name and reason for the reminder.
Commenting Technique
Don't leave commenting until the end, either do it first or as you go, otherwise
it won't get done.
A good coding technique, which promotes the logical breakdown of code and also
encourages commenting, is to first write the header comments of a high level procedure
and then write individual comments within the body of the procedure separated by blank
lines. Next write the code itself (supported by additional comments) to implement each
of the logical comments. This approach ensures that most of the comments are written first
and therefore don't get forgotten.
Choice Of Names
The choice of names used in code construction is fundamental to the writing
of self-documenting source code. We believe this to be so important that we have
dedicated an entire article to the subject entitled 'Visual Basic Naming Conventions'
which we recommend reading in addition to this article.
Source Files
Source files are the base building blocks of projects and usually the
first thing the reader will look at (most likely via the Project Explorer).
The names you select for Name attributes and filenames should be
chosen carefully to direct the reader as early as possible to the appropriate
source file. On opening a source file, the source file header comments
should clarify the source file's purpose and therefore immediately confirm
or deny the reader's expectations.
Source files should group related functions and demonstrate high levels
of cohesion. Loose coupling to other source files will promote re-use potential.
Classes should be used to encapsulate complex data.
Code Layout
Indent Code Consistently
The layout of well formatted code emphasizes the code's structure
making the logic easier to read and understand and making the code
path easier to follow. Agree the number of spaces to use per indent
level (Visual Basic defaults to 4 spaces).
Always align the following code block statements and indent statements nested within them:
Enum ... End Enum
For ... Next
Function ... End Function
If ... ElseIf ... Else ... End If
Do ... Loop Until
Do ... Loop While
Do Until ... Loop
Do While ... Loop
Property ... End Property
Select ... Case ... Case Else ... End Select *
Sub ... End Sub
Type ... End Type
While ... Wend
With ... End With
* Some developers do not align 'Case' and 'Case Else' statements with
the associated 'Select' and 'End Select' statements but instead indent them a further level.
Which ever convention you favour, ensure that convention is applied consistently.
Use Continuation Characters To Break Long Lines
Use a single statement per line and avoid using the colon (':') character to concatenate
multiple statements onto a single line. Use continuation characters to break long lines.
Make incomplete lines obvious and indent continuation lines to at least the same level as
a nested statement.
Use White Space Effectively
Use blank lines to separate logical statement blocks. Always precede line labels
with a blank line.
Always insert a blank line after the following statements:
End Enum
End Function
End If
End Property
End Select
End Sub
End Type
End With
Loop
Loop Until
Loop While
Next
Wend
Adopt A Coding Style Which Is Easy To Maintain
It is important that code is formatted consistently and avoids styles that
increase maintenance effort. The example code below shows formatting of variable
declarations with 'As' clauses aligned which, although attractive, unfortunately
causes increased maintenance effort.
Dim bExpired As Boolean
Dim dtStartDate As Date
Dim dtEndDate As Date
|
Procedures
Structure Procedures Consistently
Adopt the following template when structuring procedures:
Comment header block.
Procedure declaration.
Constants.
Variables.
Error trap statement.
Argument assertions.
... code block ...
Exit point.
Cleanup code.
Exit statement.
Error handler.
Always precede procedures with a comment header summarising the purpose of the procedure
and documenting parameter usage.
Ensure each procedure performs a specific, single task. Avoid adding parameters
to existing procedures which pervert the single purpose of a procedure, instead add an
alternative procedure to perform the desired task.
Avoid creating "monster" sized routines. Split high level tasks into logical smaller,
concise tasks. Consider the use of modelling tools which permit careful consideration of
methods and arguments prior to coding the implementation. The iterative process of modelling
can be extremely valuable prior to coding.
Name Procedures Carefully
The name of a procedure
should state exactly what the procedure does - nothing more, nothing less. Badly
named procedures often understate or simply fail to explain what the procedure
does and can therefore mislead the reader.
Assert Procedure Arguments Are Valid
Within a procedure which accepts parameters, use 'Debug.Assert' statements for arguments
that expect a particular value or limited range of inputs. The 'Debug.Assert' statements thus
function with twin purpose: providing supporting documentation of the procedure
arguments and as defensive coding
constructs when tracing code execution in the IDE.
Ensure Procedures Have Only A Single Exit Point
Procedures should always have a single exit point. Avoid using multiple
'Exit' statements within a procedure. Having a single exit point helps to
ensure that cleanup code is executed prior to exiting the procedure.
Minimise Decision Points
Seek to minimise nesting and conditional logic within procedures as much
as possible. Particularly avoid deep nesting of conditional logic as this
can lead to increased complexity.
Functions which contain deep nesting and large numbers of decision points
are best simplified by breaking down the code into multiple but simpler
procedures.
Note: See our article entitled 'Cyclomatic Complexity Metrics'
for an explanation of how to measure the complexity of your Visual Basic
source code.
Simplify Expressions And Conditional Logic
Use parentheses to clarify expressions (not everyone can or cares to remember operator precedence!).
Where appropriate, move complex boolean expressions to their own separate
functions where the logic can be isolated and documented with a meaningful function name
and a clear function header comment.
Avoid Code Bloat
Some coding styles lead to code bloat i.e. too much code. An example of this is the use of return
codes for communicating success or failure as compared to the technique of raising exceptions. Each time
a call is made to a procedure which returns success or failure, the return code must be tested and
appropriate actions taken according to the value of the return code.
If a number of such calls are made
within a single procedure then each call becomes ever more nested within the condition tests of previous
calls. The additional code to trap return codes combined with the increased levels of nesting adds to the
complexity making the procedure more difficult to understand, test and maintain.
Note: For an explanation of the benefits of raising exceptions for error handling see our article
entitled 'Make Your Error Handling Exceptional'.
Loops
Always indent the statements within the body of a loop. Try to minimise
the number of statements within the loop as loops which contain sufficient
statements to overflow screen size can become difficult to understand.
Ideally, perform a single task only within a loop making it easy to view
and the intent more obvious. Never leave the body of a loop empty and
always ensure loops are entered from the top only.
For 'For ... Next' loops, use a sensible name for the index counter and
avoid using the index counter outside of the loop itself. Avoid using enumeration
values for loop ranges.
Use Of Constants And Enums
Avoid magic numbers and string literals and use named constants instead. Use
enumerations to define named lists of valid values and avoid bit-level values
whenever possible. Always use comments to explain the purpose and values
assigned to constant and enum declarations. Sensible names can also provide
strong indications of their intended purpose.
Use Of Variables
Don't be afraid to make use of extra variables to make complex expressions,
intermediate results and conditional logic easier to understand. Avoid using
the same variable for different purposes merely to avoid declaring an extra variable.
Related Documents
Source code alone cannot be used to document all attributes of a piece of software.
Most applications are often accompanied by supporting documents such as specifications
for functional and non-functional requirements, test plans and technical documentation
describing architecture, design goals, program structure etc.
Visual Basic permits related documents to be associated with a project. To add a related document,
right click the Project Explorer window and select the "Add" menu "Add File..." option.
Select the appropriate filename and check the "Add As Related Document" checkbox. The related
document will now be listed in the Project Explorer window within the "Related Documents" folder.
Relating source code to supporting documents is more likely to allow both to remain synchronised
and also points the reader in the direction of further related information.
How VB Law Can Help
Having coding and naming conventions is one thing, enforcing them successfully is another.
To ensure coding and naming conventions are implemented correctly you may wish to extend your
VB Coding Standards and include such conventions as topics to be covered during peer code reviews.
Find out how VB Law can quickly help define and enforce VB
coding and naming conventions. VB Law has in-built support for scope tags, data type tags and
control tags and comes with sample rules to enforce both coding and naming conventions.
Visit our downloads page and trial VB Law
for free today.
Recommended Book ...
|
|
Code Complete 2, Steve McConnell
|
Our opinion: Must read! [Best read in sections, easy to re-visit.]
'A Practical Handbook Of Software Construction' is an accurate description of this book.
Packed with valuable tips on all aspects of coding it leaves no stone unturned. Some
development managers make this book mandatory reading for developers joining their team and,
after reading it, you'll understand why. If you want to avoid bad coding habits and learn how to write
better code then you won't be disappointed with this book.
|
|