Application Development
Application Standards
Enterprise Library Application Blocks
Application Unit Testing

General Coding Standards Details

1. Code layout and style are used consistently within the same file

Understandability, Learnability

Guideline

Whilst it is desirable that a strict coding layout and style be enforced for all developers to follow, the reality is usually that each developer may have their own slight variations to other developers.  This happens because developers come from different backgrounds and possibly have used different programming languages.  Each programming language community usually develops their own set of conventions and guidelines.

Areas where some variations are likely to occur include:

  • Placement of curly-braces ({) or "begin" keyword for code blocks - same line or on separate line?
  • Number of spaces used for indenting (e.g. 2, 3, or 4 spaces?)
  • Use of blank lines to separate statements within related or nested code blocks
  • Maximum length of a line of code (e.g. 70, 80, or 120 characters?)

It is important to understand that the most important rule to follow is that code formatting and style are consistent within the same file.

For example, if Developer A creates a file and prefers to use 3 spaces for indents and subsequently Developer B edits the file who happens to prefer 4 spaces, what should Developer B do?  Developer B should follow the existing style used in the file over their own preferences.

Requirement

  • Code layout and style must be consistent within each file.
  • Code layout and style can vary between different files so as long as the layout and style used does not violate of any of the other Layout and Style coding standards.

Checking Process

Examine each code file and check that code formatting and style are used consistently in each file.

References

 

Back to top

2. Indentation is accurately and consistently used to show the logical structure of the code

Understandability

Guideline

Indentation should be used by developers to show the logical structure of the code.  Logical structure here refers to the hierarchy of code constructs formed by nesting code constructs within their parent code construct.  For example, namespaces contain classes, which contain methods, which contain code blocks, which in turn contain statements or other code blocks, etc.  This logical structure is easy to understand for a computer but not so easy for a human reader without any visual cues.

Indentation is used to physically layout the code to emphasise its logical structure for human readers.  If indentation is not used or used incorrectly it makes the code harder to comprehend for developers.

Requirement

  • Logically nested code statements (including comments) must be indented one-level relative to the statement which they are subordinate to.
  • Spaces are used for indenting - not tabs.

Example

Sample using indentation (C#)
Console.Write("Enter
	an integer value: ");
 string userInput = Console.ReadLine();

int number = 0;

if (int.TryParse(userInput, out number))
{
	bool isEvenNumber = ((number % 2) == 0);

	if (isEvenNumber)
	{
		Console.WriteLine("You entered
			an even number.");
	}
	else
	{
		Console.WriteLine("You entered
			an odd number.");
	}
}
else
{
	Console.WriteLine("That's
		not an integer! Goodbye...");
}

Exemption

Long continuous lines of code can be split onto multiple lines as described in the standard Long continuous code lines are split and indented one level under the first line of the statement.  The multiple lines are logically part of the same statement but physically split over multiple lines for readability.  Lines appearing after the first line are indented as if they are subordinate to the first line.

Checking Process

Check a sample set of the supplied code to verify that:

  • A consistent one-level indent is used for each logical nesting of code statements.
  • Indentation is not used arbitrarily for statements that are not logically subordinate to the statement at the previous indent level.
  • Indenting should be done using spaces, not tabs. Check a sample of indents in a text editor to verify they are not tabs.

Note: The amount of spaces used to indent is not as important as it being consistent (see standard Code layout and style is used consistently within the same file). Typically, 2 to 4 spaces is used for indenting code.  The recommended indent amount is 4 spaces and is the default in tools such as Visual Studio .NET.

 

Back to top

3. Long continuous code lines are split and indented one level under the first line of the statement

Understandability

Guideline

Lines of code that are long can be difficult to read on a computer screen or to print correctly on paper.  In order to aid readability and/or printing of the code, long lines should be split over multiple shorter lines.  Each split line (other than the first line) should be indented one level under the first line.

How many characters is considered long?  It is difficult to enforce a specific number of columns as screen resolutions change and some developers prefer higher or lower resolutions than other developers.  The decision to split a long line is therefore subjective to the code writer and reader/reviewer.

A suggested guide is that no line should be longer than 120 characters.  If fitting code into 120 characters per line is an issue, there might be too many indentation levels which is a sign that code probably needs to be refactored.

For lines that are split over multiple lines, aim to have each line that makes up the same statement no longer than 80 characters. For example, if a line is 143 characters long try to split it as 80 + 63 characters rather than 120 + 23 characters.  However, a line that is 118 characters long can stay that length without splitting.  Note, it is not always possible to split the line at an exact character count, so choose the location that makes sense and allows correct compilation of the code.

Some languages (e.g. Visual Basic) require a line continuation character (an underscore, for example) to appear at the end of the line so that the compiler understands that the line continues onto the next line.

String usually cannot be split over multiple lines without having the string opening and closing quotes on the same line (string concatenation characters can be used to join a long string over multiple lines).  However, when printing code with long strings the printer may wrap the line onto the next line so that it fits on the paper and hence appears to continue onto the next line.  This is an artefact of the print formatting rather than the way the code appears in the file.

Requirement
  • The maximum length of a line of code must be less than or equal to 120 characters.
  • Long lines must be split over multiple lines with the split lines indented one level under the first line.
Exemption

If it is not possible to split a line without it affecting the compilation of the code then it can be exempt from splitting (however, this is seen as highly unlikely and re-factoring techniques should be applied to reduce the line length).

Checking Process

Check a sample set of the supplied code to verify that:

  • No single-line statement exceeds the maximum length of 120 characters.
  • Long statements are split over multiple lines and indented one level under the first line of the statement.
 

Back to top

4. Only one declaration or statement per line

Understandability

Guideline

Code should be structured so that only one constant/variable declaration or other code statement is used per line.  Writing code with multiple statements on a single line is often done to reduce the vertical space occupied by the code block but this reduces the readability of that code, makes finding and debugging specific statements more difficult, and does not allow commenting of individual declarations and code statements.

In Visual Basic .NET, multiple statements can be combined onto a single line by using the colon (:) character to separate the statements - do not do this.

Requirement
  • Only a single variable declaration or other code statement should be used per line.
  • Break compound statements (e.g. If...End If, For...Next, etc) over multiple lines - each part of the statement appears on a separate line and its nested statements appear on separate lines.

Example

Multiple declarations (Visual Basic)
' This is correct.
Dim studentId As Integer
Dim fullName As String

' This is wrong.
Dim studentId As Integer, fullName As String
Compound statement (Visual Basic)
' This is correct.
If studentId IsNot Nothing
	Then
	Console.WriteLine("Student Id = " & studentId)
End If

' This is wrong.
If studentId IsNot Nothing
	Then Console.WriteLine("Student Id = " & studentId) End If
Simple statements (Visual Basic)
' This is correct.
vectorX = vectorX / length
vectorY = vectorY / length
vectorZ = vectorZ / length

' This is wrong.
vectorX = vextorX / length : vectorY = vectorY / length : vectorZ = vectorZ / length
Exemption

FOR loops in languages such as C#, C++, C, and Java should be written to keep the initialisation, condition, and update sections on a single line:

for (initialisation; condition; update) {
	statements;
}

Checking Process

Check a sample set of the supplied code to verify that:

  • Individual constant/variable declarations appear on separate lines.
  • Simple code statements appear on separate lines.
  • Compound statements are broken over multiple lines such that each part of the compound statement and its nested statements appear on a separate lines.
 

Back to top

5. Each code file contains types for a single namespace only

Understandability, Learnability

Guideline

Avoid mixing type definitions for different namespaces within the same code file as this makes it harder to locate types and decreases maintainability.

Each code file may contain various types such as classes, interfaces, and enumerations within the scope of a namespace.  The types defined in each file should only contribute to the same namespace.  Related types should be logically grouped within the same namespace and also physically grouped within related files.  Mixing types from different namespaces within the same physical files may confuse the reader or maintenance developer.

Requirement

  • Each code file only contains one namespace declaration (avoid nested namespaces if the language allows this).
  • All types defined within the same code file contribute to the same namespace defined in that file.

Checking Process

For each code file:

  • Search for the keyword "Namespace" in each code file - only one namespace should be defined (for languages that require nesting of namespaces, only one leaf namespace should exist).
  • All types in the file must be defined within the scope of the one-and-only namespace (i.e. nested within the namespace).

References

 

Back to top

6. Whitespace is used consistently to improve readability of code

Understandability

Guideline

Use of blank lines between code constructs and spacing within individual code statements helps to improve readability of the code by reducing the code density or the amount of information that the brain must contend with at a time.  Spacing code out into logical groupings helps the reader understand the code more quickly.

For example, phone numbers are commonly written with spacing between groups of digits to help the reader recognise and remember the phone number by understanding the sub groups of digits.

Another example is the use of paragraphs in an letter to break the content into smaller, digestible parts.  Words in a sentence are spaced from each other rather than being written as a continuous sequence of characters.

Requirement

At least one blank line is used to separate code constructs from each other:

  • Between type definitions (e.g. classes, interfaces, enumerations, etc).
  • Between methods and properties.
  • Between class member declarations and the rest of the code that follows (e.g. constructors or methods).
  • Before a block or single-line comment.
  • Between logical sections inside a method or property to improve readability.

A single blank space should be used in the following instances:

  • After a comma in method argument lists (calling a method)
DoSomething(a, b, c, d);
  • After a comma in method signatures (parameter definitions)
public void DoSomething(int
a, int b, string c, string d)
  • Between a language keyword and the opening parenthesis
if (File.Exists(logFile))
while (count > 0)
  • Before and after all binary operators, except the period (.)
    a + b
    a && b
    a || b
    employee.Name
  • After each expression in a FOR statement (C#, C++, C, Java, etc).
for (expr1; expr2; expr3)
  • After a type cast (C#, C++, C, Java, etc)
DoSomething((int) x, (double) y);

Blank spaces should not be used in the following instances:

  • After a method name and its opening parenthesis (this helps to distinguish method calls from language keywords).
DoSomething ();
  • After the method opening parenthesis and the method arguments.
  • After the last method argument (or opening parenthesis if their are no arguments) and the closing parenthesis.
DoSomething ( a, b, c, d );
  • Inside indexers or array brackets/parentheses.
points[ index ];

Checking Process

Check a sample set of the supplied code to verify that:

  • Whitespace is used as per the above requirements for blank lines and blank spaces.
 

Back to top

7. Each file contains a single public or internal class, interface, enumeration, or other type

Understandability, Learnability

Guideline

Avoid putting multiple public or internal types such as classes, interfaces, and enumerations into the same file.

Housing each public or internal class, interface, enumeration or other type in its own file helps the developer or reader quickly find the code they are looking for, especially if the file is named after the type it contains (see standard Filename matches the name of the contained public or internal class, interface, enumeration, or other type).

Separating public and internal types into different files also helps the developer learn the structure of the code and minimises source control exclusive check-out issues arising from placing too many types into a single file.

Note: In Visual Basic internal is referred to as "Friend".

Requirement

Each source code file shall contain only one public or internal type.

Exemption

This standard only applies to languages that support code access levels such as public, protected, internal, and private.

Nested types are considered part of the parent type.

Checking Process

Check each source file for the presence of more than one public or internal type at the global or namespace level.

References

 

Back to top

8. Boolean conditions are not explicitly evaluated against true or false

Understandability

Guideline

Avoid explicitly evaluating Boolean conditions against the values true or false (or the equivalent values in chosen programming language).

Instead, use implicit evaluation of Boolean conditions to either true or false.  Explicit evaluation against the values true or false is redundant, results in longer Boolean expressions, is harder to understand, and can introduce errors in logic.

When using binary operators such as less than (<) or greater than or equal to (>=) the Boolean condition is evaluated implicitly. Therefore, using implicit evaluation for other Boolean conditions is more consistent than using explicit evaluation.

Note: Some languages do not support the built-in Boolean type (e.g. ANSI C) and rely on the programmer to define values for true and false, respectively (via constants or pre-processor definitions).  Typically, the value 0 is false and a non-zero value is true (e.g. 1 or -1).  Boolean conditions can still be evaluated implicitly in these languages as long as all expressions, functions, and overridden operators return the appropriate values representing true and false.

Requirement

Boolean conditions use implicit evaluation rather than explicit evaluation against the values for true or false.

Example

Boolean variable (Visual Basic)
' Example of correct way.
If Not isValid Then
	' TODO: Do something here...
End If

' Example of wrong way.
If isValid = False Then
	' TODO: Do something here... 
		End If
Binary operator (Visual Basic)
' Example of correct way.
If firstName.Length > 0 
	Then
   ' TODO: Do something here...
End If

' Example of wrong way.
If (firstName.Length > 0) = True
	Then
   ' TODO: Do something here...
End If

Checking Process

Check a sample set of the supplied code to verify that:

  • Boolean conditions in control statements (e.g. If, While, etc) are not explicitly evaluated against true or false values.
    • Source code can be searched for "= True" or "= False" in Visual Basic (or equivalent for other languages)

References

 

Back to top

9. Code elements are not aligned vertically to columns or tab stops (other than for normal indentation)

Complexity

Guideline

Avoid using "pretty" layout techniques to align related keywords or tokens on multiple lines to the same columns or tab stops.  This type of layout can be difficult and tedious to maintain.

A good code layout should withstand modifications such that modifying one line of code should not require modifying several others.

Requirement

Do not space related code elements so that they align vertically to a specific column or tab stop.

Example

Low maintenance example (Visual Basic)
Public Sub AddAddress(
	street As String,
	suburb As String,
	city As String,
	postCode As String,
	latitude As Double,
	longitude As Double)
	' TODO: Code for AddAddress goes here...
End Sub ' A new parameter
	is added to the end of the parameter list. ' This change only involves replacing
	the closing parenthesis ' with a comma (,) after the last parameter and then
	adding the ' new parameter followed by a closing parenthesis.
Public Sub AddAddress(
	street As String,
	suburb As String,
	city As String,
	postCode As String,
	latitude As Double,
	longitude As Double,
	someVeryLongVariableName As String)
	' TODO: Code for AddAddress goes here...
End Sub
High maintenance example (Visual Basic)
Public Sub AddAddress(
	street  As String,
	suburb  As String,
	city  As String,
	postCode  As String,
	latitude  As Double,
	longitude As Double)
	' TODO: Code for AddAddress goes here...
End Sub ' A new parameter
	is added to the end of the parameter list. ' This results in modifying all the
	previous parameters ' by adding spaces to vertically align the "As [Type]"
	definitions.
Public Sub AddAddress(
	street  As String,
	suburb                   As String,
	city  As String,
	postCode  As String,
	latitude  As Double,
	longitude  As Double,
	someVeryLongVariableName As String)
	' TODO: Code for AddAddress goes here...
End Sub

Exemption

Normal use of indentation to show the logical structure of nested code elements is acceptable and required as per standard Indentation is accurately and consistently used to show the logical structure of the code.

Checking Process

Check a sample set of the supplied code to verify that:

  • Extra spaces or tabs are not used to space related code elements so that they align vertically to a specific column or tab stop.
 

Back to top

10. Correct casing is used for all identifiers

Understandability, Learnability

Guideline

Use of consistent casing for identifiers improves readability and creates familiarity for developers and reviewers of code.

The two types of casing used are:

  • Pascal casing - first letter of each word is capitalised and the remaining letters are in lower case:
MotorVehicle
  • Camel casing - first letter of first word is in lower case then the remaining words follow Pascal Casing rules:
employeeDetails

In general, Pascal casing is used for type definitions, methods and properties where as Camel casing is used for instances of types (variables, constants, parameters).

Requirement

Pascal casing is used for:

  • Namespaces
  • Classes and Structures
  • Interfaces (see Exemption section)
  • Enumerations and their options
  • Delegates
  • Methods (Subroutines/Functions/Procedures, etc)
  • Events

Camel casing is used for:

  • Class/Structure member variables and constants
  • Global variables and constants
  • Method local variables and constants
  • Method parameters
  • Class properties
  • Delegate parameters

Any acronyms of three or more letters should be Pascal case, not all capitals:

  • TVGuide is correct
  • XMLParser in not correct, should be XmlParser

The following is not to be used:

  • All capitalisation (e.g. DAYS_IN_WEEK)
  • Underscore (_) or hyphen (-) characters to separate words (class members should be prefixed with an underscore, see Exemption section)

Exemption

Interfaces are prefixed with a capital "I" then the rest of the name follows Pascal casing to allow easy distinction from classes:

Interface declaration (Visual Basic)
Public Interface IEmployee
	' TODO: Define methods and properties of IEmployee interface
		here...
End Interface

' Create a class that implements the IEmployee interface.
Public Class Employee : Implements
IEmployee
   ' TODO: Implement IEmployee interface methods and properties...
End Class

Class members are prefixed with an underscore (_) character to avoid qualifying class members with this (Me in Visual Basic).  For case insensitive languages such as Visual Basic, class members and property names cannot vary by case alone so the underscore also helps in this situation.

Cases21 code can use all capitalisation for identifiers as long as it is done consistently (in this case all code keywords should also be capitalised as well).

Languages which are case insensitive (except Visual Basic) which have a code editor that forces all typed text into a specific case are exempt provided the developer cannot customise this behaviour and if casing for all code keywords and identifiers is used consistently.

Checking Process

Check a sample set of the supplied code to verify that:

  • Identifier casing is used as per the above requirements for Pascal casing and Camel casing.
  • Interfaces start with a capital "I" then follow Pascal casing (see Exemption section)
  • Class member variables are prefixed with an underscore (_) and then follow Camel casing (see Exemption section)

References

 

Back to top

11. Filename matches the name of the contained public or internal class, interface, enumeration, or other type

Understandability, Learnability, Traceability

Guideline

The name of each source file should correspond to the name of the contained public or internal (Friend in Visual Basic) type. This standard is related to the standard Each file contains a single public or internal class, interface, enumeration, or other type.

Naming the file after the type it contains helps developers and readers quickly understand the structure of the code and find the location of specific types.

Requirement

Each source code file should be named after the contained public or internal type.

Example

File: IEmployee.vb (Visual Basic)
Public Interface IEmployee
	' TODO: Define methods and properties of IEmployee interface
		here...
End Interface
File: DaysOfWeek.vb (Visual Basic)
Public Enum DaysOfWeek
	Sunday,
	Monday,
	Tuesday,
	Wednesday,
	Thursday,
	Friday,
	Saturday
End Enum
File: DatabaseException.vb (Visual Basic)
Public Class DatabaseException : Inherits
Exception

   Public Sub New()
	  MyBase.New()
   End Sub Public Sub New(ByVal message As String)
	  MyBase.New(message)
   End Sub Public Sub New(ByVal
   message As String, ByVal
   innerEx As Exception)
	  MyBase.New(message, innerEx)
   End Sub 
End Class

Checking Process

For each code file:

  • Check that the file name corresponds to the name of the contained public/internal type.

References

 

Back to top

12. Hungarian Notation is not used for code variables and constants

Understandability, Learnability, Complexity

Guideline

Hungarian Notation is a naming convention which encodes some metadata about the variable in its name such as the data type or intended use.

Avoid using Hungarian Notation for code variables and constants because it:

  • Decreases readability of code by introducing cryptic abbreviations
  • Imposes unnecessary maintenance overhead on developers
  • Introduces another thing that developer must learn and adhere to
  • Can be used inconsistently by people not familiar with the notation (custom derivatives are common)
  • Exposes underlying implementation (e.g. its data type) or encodes its intended use (e.g. to hold a string) which can change later

In modern Integrated Development Environments (such as Visual Studio) this information is redundant since the developer can tell the type of a identifier through tooltips and IntelliSense.

Instead of using Hungarian Notation, code variables and constants should use meaningful names which convey the purpose they serve rather than how they are used.

Some Hungarian prefixes include:

  • str (string)
  • b (byte)
  • f (floating point)
  • p (pointer)
  • rg (array)
  • m_ (class member)
  • C or cls (class)

Example

Class member of type string
m_strName
Array of floating-point values
rgfStockPrices
Hungarian Notation sample from this 
	MSDN Library article
1   #include "sy.h"
2   extern int *rgwDic;
3   extern int bsyMac;
4   struct SY *PsySz(char sz[])
6      {
7      char *pch;
8      int cch;
9      struct SY *psy, *PsyCreate();
10      int *pbsy;
11      int cwSz;
12      unsigned wHash=0;
13      pch=sz;
14      while (*pch!=0
15         wHash=(wHash<>11+*pch++;
16      cch=pch-sz;
17      pbsy=&rgbsyHash[(wHash&077777)%cwHash];
18      for (; *pbsy!=0; pbsy = &psy->bsyNext)
19         {
20         char *szSy;
21         szSy= (psy=(struct SY*)&rgwDic[*pbsy])->sz;
22         pch=sz;
23         while (*pch==*szSy++)
24            {
25            if (*pch++==0)
26               return (psy);
27            }
28         }
29      cwSz=0;
30      if (cch>=2)
31         cwSz=(cch-2/sizeof(int)+1;
32      *pbsy=(int *)(psy=PsyCreate(cwSY+cwSz))-rgwDic;
33      Zero((int *)psy,cwSY);
34      bltbyte(sz, psy->sz, cch+1);
35      return(psy);
36      }

Requirement

Code variable and constant identifiers must not use Hungarian Notation or custom derivatives which encode metadata such as data type and intended use (e.g. to hold a string) in the identifier name.

Exemption

Interfaces can be prefixed with a capital "I" to allow easy distinction from classes (refer to Exemption section of standard Correct casing is used for all identifiers).

Class members can use the underscore (_) prefix to avoid naming conflicts with property names and method/constructor parameter names (refer to Exemption section of standard Correct casing is used for all identifiers).

Checking Process

Check a sample set of the supplied code to verify that:

  • Code variable and constant identifiers do not use Hungarian Notation or derivatives.  This can be checked by searching for common Hungarian Notation prefixes or visually inspecting a sample of variable and constant identifier names for prefixes which appear to encode metadata.

References

 

Back to top

13. Methods and properties do not expose implementation details but rather explain their intent

Understandability, Learnability, Complexity

Guideline

Method and property names should use meaningful names that help the developer understand their purpose.  Meaningful names help code be self-documenting and avoids ambiguity surrounding the actual intended purpose of a method or property.

Avoid exposing implementation details when naming methods and properties such as the type of underlying data or specific algorithm used.

Method name examples (Visual Basic)
Public Class StudentNameCollection

	Private _studentNames() As
		String

	' Example of a meaningful name that explains the purpose
		of the method.
	Function GetNextStudentName() 
		As String
		' TODO: Implementation goes here...
	End Function End Class

Public Class StudentNameCollection

	Private _studentNames() As
		String

	' Example of a poor name that exposes implementation details.
	Function GetNextArrayElement() 
		As String
		' TODO: Implementation goes here...
	End Function End Class

Requirement

  • Method and property names should not expose implementation details in their name.
  • Method and property names should be named to explain their intent.

Checking Process

Check a sample set of the supplied code to verify that:

  • Method and property names does not expose implementation details.  Hint: Check for data type or algorithm names in the method and property names.
  • Method and property names appear to be meaningful and convey their purpose in the context of the class or interface that are part of.

References

 

Back to top

14. Standard naming scheme is used for all code constructs

Understandabillity, Learnability, Traceability

Guideline

A standard naming scheme should be used for code constructs such as classes, interfaces, methods, and properties.

Using a standard naming pattern helps developers create meaningful and readable names which helps others to understand and learn the code more quickly.  It also introduces a level of consistency over arbitrarily naming code constructs.

Classes and Interfaces naming scheme

  • Commonly represent "things", entities, objects, etc, so they should use nouns:
    • Flight
    • Person
    • Employee
    • Customer
  • Often a noun must be qualified with an adjective or a verb to further describe or constrain the entity:
    • LinkedList (Linked is an adjective)
    • RandomAccessFile (Random is a verb, Access is an adjective)
  • A noun can also be qualified by another noun:
    • JpegImage (Jpeg is a noun)
  • A class or (more often) an interface is sometimes an adjective (called a structural property as they confer specific structural properties upon their subclasses) , often suffixed with -able:
    • Comparable
    • ISerializable
    • Drawable
    • IPrintable

Methods naming scheme

  • Methods should use a verb-noun naming scheme (the noun can be plural):
    • CalculateInvoiceTotal (correct example)
    • CloseDialog (correct example)
    • EmployeeFinder (incorrect example)
    • SalaryCalculate (incorrect example)
  • Methods (Subroutines, Functions) are supposed to represent an "action" (i.e. a verb) that an entity can perform.
  • The noun is used to constrain the action to a specific part or attribute of the entity.
  • Some variations:
    • A verb can be used on its own:
      • Save
      • Open
      • Dispose
    • A noun can be qualified with prefixed adjectives:
      • CalclulateCheapestFlight
    • A noun can be qualified by a suffixed preposition ("By", "With", etc) and a noun:
      • GetEmployeeByID
      • GetEmployeesWithHighSalaries

Properties naming scheme

  • Properties represent an attribute of an entity.  They should use a noun naming scheme which can be plural:
    • Price
    • CarAccessories
  • They are not to be used for performing actions - use methods for that.
  • For languages that do not support properties, a pair of methods can be used where the accessor is prefixed with Get and the mutator is prefixed with Set:
    • GetPrice()
    • SetPrice()

Requirement

  • Classes, structures, and interfaces should use the qualified-noun naming scheme (see Guideline section).
  • Methods should use verb-noun naming scheme (see Guideline section).
  • Properties should use the noun naming scheme (see Guideline section).

Exemption

Structural Property classes and interfaces should can use adjective and a suffix such as -able.

Checking Process

Check a sample set of the supplied code to verify that:

  • Classes, structures, interfaces, methods, and properties use the standard naming scheme described in the Requirement section.
 

Back to top

15. Code folder structure should correspond to the namespace used within the source file

Understandability, Learnability

Guideline

Each source code file should reside in a hierarchical folder structure than resembles the namespace structure used in the file.

For example, a file named JetPack.vb with with a public class named JetPack residing in the namespace Acme.Gadgets should be located in:

  •  $(ProjectDir)/Acme/Gadgets.

Requirement

Source code files should be located in a folder structure that mimics the namespace structure used in the file.

Checking Process

Check that each file is located in a folder structure that mimics the namespace structure used in the file.

References

 

Back to top

16. Each user-defined data type has a description of its purpose

Understandability, Learnability, Complexity

Guideline

User-defined types such as classes, structures, interfaces, enumerations, etc, should have an accompanying description outlining their purpose and intended use.  Whilst a user-defined type should always have a meaningful name, a description using comments can provide further detail about the purpose and usage of the class (e.g. via examples).

Even a concise comment (e.g. a single line) can help other developers and code reviewers better understand the purpose of the type.

These comments form part of the API documentation and as such should be considered important.

Requirement

  • Each user-defined type is accompanied by a description of it purpose.
  • The description should appear in comments just above the first line of the type definition.

Example

Class comments (Visual Basic XML documentation style)
/// <summary>
/// This class represents
	a customer account with the bank.
/// It can be used for
	performing operations on the account
/// such as deposits,
	withdrawals, transfers, and balance enquiries.
/// </summary>
public class BankAccount
{
	// TODO: Implementation goes here...
}

Checking Process

For every user-defined type, check that a description of its purpose is included as per the Requirement section above.

References

 

Back to top

17. Each method/property has a description of its purpose, parameters, returned values, and error conditions

Understandability, Learnability, Complexity

Guideline

Each method and property should be accompanied by a description of its purpose and error conditions.  Methods should also include a description of their input/output parameters and any returned values.

Whilst methods and properties should always have a meaningful names, a description using comments can provide further detail about their purpose and usage.

Even a concise comment (e.g. a single line) can help other developers and code reviewers better understand the purpose of a method property.

These comments form part of the API documentation and as such should be considered important.

Requirement

  • Each method or property is accompanied by a description of it purpose.
  • Each method or property is accompanied by a description of any error conditions (e.g. invalid arguments that can raise an exception).
  • Each method is accompanied by a description of all input/output parameters and any return values (if applicable).
  • The description should appear in comments just above the first line of the method or property definition.

Example

Method comments (Visual Basic XML documentation style) 
/// <summary>
/// Transfers funds from
	this bank account to another bank account. /// After a successful transaction, this account's balance will
		have reduced by the
/// transferred amount.
	The destination account will have increased by the transferred
/// amount.
/// </summary> /// <remarks>
/// If the destination
	account is in a different currency to this account then
/// a currency conversion
	will take place prior to depositing the funds into
/// the destination account.
/// </remarks>
/// <param name="destinationAccount">Destination bank account to receive the transferred funds.</param>
/// <param name="amount">Amount
	of money to transfer from this account to the destination account.</param>
/// <exception cref="MyBank.Accounting.InsufficientFundsException">
/// Thrown when the amount
	requested for transfer is greater than the balance of this account.
/// </exception>
/// <exception cref="MyBank.Accounting.SourceAccountUnknownException">
/// Thrown when there
	is no record of an account matching the account number of this account object.
/// </exception> /// <exception cref="MyBank.Accounting.DestinationAccountUnknownException">
/// Thrown when there
	is no record of an account matching the account number of the destination
/// account object.
/// </exception> /// <exception cref="MyBank.Accounting.SourceAccountNotActiveException">
/// Thrown when this account
	is not active (e.g. is has been suspended).
/// </exception> /// <exception cref="MyBank.Accounting.DestinationAccountNotActiveException">
/// Thrown when the destination
	account is not active (e.g. is has been suspended).
/// </exception>
public void TransferFunds(BankAccount
destinationAccount, double amount)
{
	// TODO: Implementation goes here...
}
	  

Checking Process

For every method or property, check that a description of their purpose, parameters, return values, and error conditions (if applicable) are provided as per the Requirement section.

 

Back to top

18. End-line comments should not to be used, except for declarations

Complexity

Guideline

Comments should not be placed at the end of a line of code ("end-line" comments) except for commenting declarations.

End-line comments are not recommended since they can extend off the screen and make the code harder to read. Putting inline comments on a separate line makes it easy to read the comments and its easy to separate code from comments. This approach also makes it easier to read code when using printouts or an editor that does not support colour coding.

Requirement

End-line comments are not used for commenting code except for commenting declarations.

When commenting declarations with end-line comments:

Exemption

Declarations, such as class members and local method variables can use end-line comments provided the comment appears on a single line only.

Checking Process

Check a sample set of the supplied code to verify that:

  • End-line comments are not used, except for commenting on declarations as per Requirements section.
 

Back to top

19. Comments are structured as sentences with proper grammar and punctuation and without spelling mistakes

Understandability, Learnability

Guideline

Documentation provided in code comments should be structured using proper sentences with correct English grammar and punctuation.  This is especially important for comments that form part of the public Application Programming Interface (API) documentation.

Ill-structured sentences without punctuation and incorrect grammar will only confuse other developers and code reviewers.  Try to write comments in a specific context or provide an example to avoid ambiguity.  Use short sentences with simple language where possible.

Requirement

  • Comments are written as normal sentences starting with a capital letter and ending in a period/full stop.
  • Formal language is used rather than slang.
  • There should be no obvious spelling mistakes (as would be detected from a spell checker).
  • Proper grammar and punctuation is used (as would be expected from someone with a basic understanding of the English language).

Exemption

Use of abbreviations that commonly appear in computer and Information Technology literature is acceptable.  Lesser-known abbreviations should be avoided or defined in full at least once in each file they are used.

Checking Process

Check a sample set of the supplied code to verify that:

  • Comments are written properly as described in the Requirements section.
  • Particular emphasis should be placed on the public API documentation over inline code comments.

References

 

Back to top

20. Commented-out code is removed from source code files

Understandability

Guideline

Code which has been commented out does not contribute to compiled production code and should be removed.  Commented-out code may confuse readers as to its purpose.  It may also appear harmless and inappropriately be uncommented by unwary developers which can introduce bugs.

If a history of code changes is desired, source control should be used (e.g. Visual Source Safe) for this purpose.  Source control allows a full history of changes to be examined without polluting the code with unnecessary baggage.

Requirement

  • Commented-out code do not appear in source files.

Exemption

Example code may appear in normal comments for illustration purposes.

Checking Process

Check a sample set of the supplied code to verify that:

  • Commented-out code does exist in any of the files.

References

 

Back to top

21. Global variables are not used

Understandability, Learnability, Complexity, Modularity, Scalability

Guideline

Global variables are variables that are accessible from anywhere in your code. They are not scoped like local variables to just a single class, method, or code block.

Use of global variables is generally considered bad practice because the variables can be potentially modified from any part of the program and it may not be obvious which parts of the code are dependent those variables.  Using global variables makes code harder to read and understand and may require examining a large portion of the code base to fully understand the dependencies.

Other reasons to not use global variables:

  • They increase coupling between components that make use of the global variables.
  • They make unit testing of code harder as the code cannot be isolated completely.
  • Changes to global variables may be overwritten in unsynchronised multi-threaded environments.
  • Excessive use of global variables in synchronised multi-threaded environments creates thread contention issues (thread are blocked waiting for access to the global variables).
  • Seemingly innocent changes can have a ripple effect through the application if there are many dependencies on the global variables.
  • Tracking down bugs in code that uses global variables can be difficult as the global variables can be potentially be modified from anywhere in the application.

Global variables can be present in other forms such as:

  • Public static class member variables.
  • Public static read/write properties on classes.
  • Singleton classes (which keep internal state) and are directly accessed from anywhere in the code.
  • Public Module member variables and read/write properties (Visual Basic only, see Module Statement in MSDN Library).

Static classes with helper/utility methods that do not keep state are not considered to be "global variables".

Requirement

  • Global variables not used in code.
  • Public static class member variables are not used.
  • Public static read/write properties are not used in classes.  Static read-only properties can be used as long as they are initialised once only in the application.
  • Public Module member variables and read/write properties are not used (applies to Visual Basic .NET only).

Exemption

For languages that are not Object-Oriented, some global variables may be required (e.g. for use in the main entry point function).  These variables should not be accessed globally but rather passed as parameters to subroutines and functions in other modules.  Any updates required should be returned from the subroutines (as out parameters) or functions. The top-level code should be responsible for updating the global variables, not the subroutines and functions located in other modules. This makes the code less coupled to specific global variable names.

For Object-Oriented languages, classes based on the Singleton design pattern can be used as long the Singleton objects are passed to dependent methods as parameters or via the constructor for dependent classes.  Typically, an interface should be provided rather than passing the concrete type as a parameter (see dependency injection).

Checking Process

Check for variable declarations not defined within the scope of a class or structure.

Check for public static variables defined in classes.

Check for read/write static properties defined in classes.

check for public member variables and read/write properties in Modules (applies to Visual Basic .NET only).

References

 

Back to top

22. Namespaces are used to organise related types

Understandability, Learnability

Guideline

Namespaces (called packages in Java) are code constructs that act as logical containers for types so that they can be organised hierarchically much like folders in a file system are used to organise files.

Namespaces provide a way to avoid collisions between type names. A type with the same name can appear in different namespaces (think of a filename that can appear in multiple folders and subfolders).

Not using namespaces and placing all types in the default or global namespace leads to namespace pollution.  This leads to frequent collisions between type names and frustration for developers.

When a type resides in a namespace its fully qualified name is the type name prefixed by the namespace.

Sample namespace or package (Visual Basic, C#, Java)
DemoCompany.DemoProduct.DemoFeature
Sample namespace (C++)
DemoCompany::DemoProduct::DemoFeature
Sample fully-qualified type name (Visual Basic, C#, Java)
DemoCompany.DemoProduct.DemoFeature.DemoClass
Sample fully-qualified type name (C++)
DemoCompany::DemoProduct::DemoFeature::DemoClass

Types can be used without their fully qualified namespace when the type name is unique within the current source file by importing the type in the file header (via Imports [Visual Basic], import [Java] using [C#],  using namespace [C++]).

Requirement

All types defined in source files are defined within an explicitly named namespace.

Checking Process

Check that all types in all files are defined within an explicitly named namespace.

References

 

Back to top

23. Enumerated types are used over a fixed set of constant values for method parameters and return types

Understandability, Complexity

Guideline

When using a fixed set of values as method parameters or return types use a enumeration over a fixed set of constants.

Enumerations logically groups a set of values under one type name, are type safe, and constrain the allowed values to a fixed set.  Using a set of constants provides no logical grouping and any value which is of the same type as the constants can be passed as a parameter.

Set of related constants (Visual Basic)
Public Const DatabaseActionInsert As
	Integer = 1
Public Const DatabaseActionUpdate As
	Integer = 2
Public Const DatabaseActionDelete As
	Integer = 3

' Example of passing constant to a method.
databaseHelper.Execute("Employee", employeeRecord, DatabaseActionInsert)

' This will not generate a compilation error.
databaseHelper.Execute("Employee", employeeRecord, 4)
   
Enumeration (Visual Basic)
Public Enum DatabaseAction
	Insert = 1
	Update = 2
	Delete = 3
End Enum ' Example of
	passing constant to a method.
databaseHelper.Execute("Employee", employeeRecord, DatabaseAction.Insert)

' This _will_ generate a compilation error since the third
	parameter ' must be of type DatabaseAction.
databaseHelper.Execute("Employee", employeeRecord, 4)

' If the enumeration value 'Insert' is removed
	from the type DatabaseAction ' then the line below will fail compilation.
databaseHelper.Execute("Employee", employeeRecord, DatabaseAction.Insert)

Note: Specific enumeration option values can be omitted if they are not important in which case the compiler auto numbers them (applicable to .NET languages).

Requirement

Use enumerations over a set of constant values when the set of values is closed (not open to extension).

Exemption

Languages that do not support enumerations as an integral type are exempted.

In Java prior to version 1.5, there is a design pattern ("Typesafe Enum") that can be used to mimic the behaviour of enumerations (post version 1.5, enumerations are supported as a regular class).

Checking Process

Check source code for sets of related constants (usually prefixed with a common name) that can be replaced by an enumeration.

References

 

Back to top

24. Constants or enumerations are used instead of literal values or "magic numbers"

Understandability, Learnability

Guideline

Literal values should be avoided in source code as it may not be clear to other developers or code reviewers as to what the values represent.  Additionally, if the same literal value is used repeatedly and it is later decided that the value should change, it is a large maintenance activity to replace all instances of the literal. This process is also error prone since the same literal value could be used in different contexts making it difficult to determine which literals belong to the same logical constant.

"Magic numbers" are numeric literal values that are used in source code.

Constants or enumerations should be used instead of literal values for the following reasons:

  • Constants and enumerations capture the intent of the values by using descriptive names.
  • Constants and enumerations have additional comments describing their purpose (where they are declared).
  • Changes to literal values need only be changed in one location and changes are automatically propagated at the next recompile.
  • Changing literal values becomes a less error prone process since only one value needs to be changed.
  • Enumerations provide a further level of type safety.

Requirement

Literal values or "magic numbers" should not be used through source code - constants and enumerations should be used instead.

Exemption

Strings are often defined in resource files rather than as constants, especially when the strings are presented to users.  The main advantages of this approach are to support other languages and to separate large numbers of strings from the code.

Checking Process

Examine supplied source code for literal values such as strings (easily identified by quotes around the string literals) or numeric values such as 5, 1.3, etc.

Literal values should only be defined in constants or enumerations (except for strings which can be defined in resource files instead).

References

 

Back to top

25. Application settings that can change on deployment are not hard coded - they are stored in a configuration file

Maintainability, Complexity

Guideline

Application settings which are dependent on the deployment environment should not be hard coded in the source code.  Typically, these would include server and database names and connection strings.

These type of settings should be made external to the source code by placing them in a configuration file which is then loaded by the application at run-time.

Application settings can also be placed in the Windows Registry but this is not recommended since the settings are not deployed in the same folder as the application (which is the case with configuration files) and there can be security issues with accessing the registry.

Requirement

Application settings that are dependent on external resources such as server and database names and connection strings should be placed in a configuration file, not hard coded in the source code (this includes constants).

Checking Process

  • Check supplied files for evidence of a configuration file with application settings.

References

 

Back to top

26. Gotos and labels are not used (where applicable)

Understandability, Learnability, Complexity

Guideline

Goto statements (with corresponding labels for jump locations) should not be used in source code as there are more structured alternatives in modern programming languages.

Gotos increase code complexity and can be confusing if abused as a single label can be used by multiple Goto statements in very unstructured ways (called "spaghetti code").

Other reasons for not using Goto statements:

  • They do not emphasise the logical structure of the code.
  • They can interfere with compiler optimisations (since optimisations often take advantage of locality of code statements and try to predict the next code path).
  • They violate the principle that code should flow strictly top to bottom (within a code block).
  • They make it harder to validate that code is correct.

Requirement

  • Do not use Goto statements in source code.
  • Do not use On Error Goto statements (applicable to Visual Basic error handling).

Exemption

Languages that do not support structured control constructs such as While loops and If conditions or structured exception handling can use Goto statements.

In Visual Basic 6, there is no structured exception handling so On Error Goto can be used for error handling.  Goto statements should not be used elsewhere in Visual Basic 6.

Checking Process

Check source code for the "Goto" keyword.

Check that all found Goto statements are not exempt (as per Exemptions section).

References

 

Back to top

27. Class member variables are initialised in constructors, not where they are declared

Complexity, Understandability, Fault Tolerance

Guideline

Class members should not be constructed or initialised where they are declared (except for constants).  Use constructors for initialising class members. Static constructors should be used for initialising any static (Shared in Visual Basic) member variables.

Placing initialisation in constructors rather than at declaration provides the following benefits:

  • Code is more readable since the developer can see the class members on their own without their initialisation code and values.
  • Constructors allow initialisation steps that cannot be performed in one line of code.
  • Performing initialisation in the constructor allows structured exception handling and error logging to be used if initialisation fails.

Complex initialisation (such as accessing a database) should not be performed in constructors.  A separate initialisation method should be provided or the initialisation data should be passed in via a constructor argument (dependency injection).

Requirement

  • Class members are initialised in constructors, not where they are declared.
  • Static class members are initialised in a static constructor, not where they are declared.

Example

Exemption

Only applies to Object-Oriented programming languages.

Checking Process

For each class in the source code files:

  • Check that all class members (including static ones) are not initialised where they are declared.
  • Check that class members are initialised in constructors.
  • Check that static class members are initialised in a static constructor.

References

 

Back to top

28. Polymorphism is used to handle sub-types of a class rather than case/switch/if statements

Maintainability, Complexity

Guideline

Often there are times when different actions need to be performed based on a sub-type of a class.  A poor choice for implementation is to use a series of If...Else If (or Case/Switch) statements to explicitly check for each known sub-type and then execute some code appropriate for that sub-type.  This can also be achieved without sub-types by using an enumeration and checking for each possible enumeration value.

For small closed sets of values where an enumeration has been used this approach is acceptable.  However, if the set is open to change it is not a good approach (in which case enumerations should probably not have been used in the first place).

When performing different different actions based on a class sub-type, the preferred method is to use Polymorphism.  This is a core Object-Oriented concept and not exploiting it often means incorrectly programming in an object-oriented language.

The benefits of the polymorphism approach are:

  • There is less code to write as polymorphism is a built-in feature of object-oriented languages.
  • Code becomes simpler to understand and easier to maintain.
  • Less code (often none of the existing code) needs to be updated when new sub-types are introduced (this is not the case when Case/Switch/If statements are used).
  • Code changes tend to be localised when new sub-types are introduced.  When not using polymorphism, changes can ripple throughout the code and it is not easy to change all dependent instances of Case/Switch/If statements.

Requirement

Use polymorphism for performing different actions based on sub-types of a common base type.

Do not use Case/Switch/If statements to determine the sub-type of a class for performing a sub-type specific action.

Do not use enumerations for representing sub-types of a class.

Exemption

Only applies to Object-Oriented programming languages.

Case/Switch/If statements can be used when not performing similar actions only differentiated by sub-types.

Case/Switch/If statements can be used with enumerations for small closed sets of values.

Checking Process

Check a sample set of the supplied code:

  • Look for long chains of If...Else If or Case/Switch statements that perform similar actions based on a sub-type of a common base class or an enumeration (whose options would satisfy the Liskov substitution principle had they been converted into a class hierarchy).
 

Back to top

29. Class member variables are not directly accessible by client code

Modularity

Guideline

Class member variables should not be directly accessible by client code as this breaks the encapsulation or information hiding principles of object oriented programming. Clients that do not hide their internal class member variables expose implementation details and run the risk of propagating changes to clients when implementation details change.  Clients should access classes via a defined interface or contract without having knowledge of the implementation.

When using .NET languages, properties can be used to provide a layer of abstraction between clients and class member variables.  In Java and C++, pairs of Get/Set (accessor and mutator) methods can be used and behave similar to properties.

If property semantics are not required, then methods should be provided to perform actions and possibly return results to the client.

Requirement

  • Class member variables should be private (preferred) or protected.
  • When property semantics are required, properties or Get/Set (accessor/mutator) methods should be provided to hide implementation details and prevent direct access to class variables by clients.

Exemption

Only applies to Object-Oriented programming languages.

Checking Process

Check all classes in the source code for member variables that are not private or protected.

References

 

Back to top

30. References to internal class members are not returned from class methods and properties

Security, Fault Tolerance

Guideline

Class methods and properties should not return references to internal class members because clients can then make modifications to the internal class members via the returned reference (or pointer in C++).

As an alternative to returning a class member directly, consider:

  • Returning a copy of the class variable.
  • Providing methods that operate on the class members (or a portion of the class member for arrays and collections) and return a copy of the member variable or some other derived result.

When working with one element of an array or collection at a time, a single value can be returned rather than the whole array or collection object.

  • C# supports indexers and Visual Basic supports the default Item property to allow array semantics to be used on classes.

Note: Even objects marked as ReadOnly (Visual Basic .NET) or final (Java) can still be modified via any public properties or methods if they return a reference to a class member object.

Requirement

  • References (or pointers for C++) to internal class member objects (including arrays) should not be returned from methods and properties.
  • If a class member needs to be returned, a copy should be returned.

Exemption

Primitive types such as Integer, Double, Boolean, as well as Value Types can be returned from methods and properties.

Immutable types (such as String in Visual Basic .NET) can be returned safely.

Checking Process

Check a sample set of the supplied code to verify that:

  • References to class member variable objects are not returned from methods or properties.

References

 

Back to top

31. Structured exception handling is used instead of return codes for managing application errors

Fault Tolerance, Complexity

Guideline

Structured exception handling is the preferred error reporting and handling mechanism in languages that support exceptions.  The alternative is to use return codes where a special integer value is returned that represents a specific application error.

Structured exception handling should be used over return codes for the following reasons:

  • Return codes are not object-oriented (exceptions can form an exception hierarchy and expose properties and methods).
  • Return codes cannot be returned from constructors, operator overloads, and properties (exceptions are out-of-band, meaning they are not part of the constructor/method/property signature).
  • Return codes do not capture any contextual information such as stack traces, contextual error messages, or input arguments that were associated with the error.
  • It is more difficult to have common cleanup code (that always executes, regardless of whether an error occurred or not) when using return codes.  Structured exception handling provides the Finally block for this purpose.
  • Erroneous conditions can propagate through the system causing the system to crash or corrupt data if return codes are not checked.  Exceptions, on the other hand, prevent erroneous conditions from continuing as the executing thread is suspended and calling code is provided an opportunity to handle the exception (unhandled exceptions will eventually propagate to the top-level and cause the application to shutdown).

For Visual Basic .NET, use Structured Exception Handling by using Try...Catch...[Finally] rather than On Error Goto.

Requirement

  • Do not use return codes (whether returned from a function or as an output parameter) for reporting and handling errors.
  • Use structured exception handling for reporting and handling errors.
  • If return codes are returned from third-party code and the error needs to be propagated to other parts of the code, wrap the return code in a custom application exception.

Exemption

Boolean methods which can detect an erroneous state which would otherwise cause an exception to be raised can and should be used/provided where possible.  However, these should compliment the existing exception handling - not replace it.

If third-party source code or libraries are used which use return codes then the application can handle errors by checking return codes.  However, return codes should be wrapped in a custom application exception if the error needs to be propagated further.

Checking Process

Examine the supplied code:

  • Search the code for terms such as "return code" or  "error code".
  • Check that error conditions are not reported via return values or output parameters.
  • Check that return codes from third-party code are wrapped in custom application exception if those errors are propagated to other parts of the code.

References

 

Back to top

32. Unhandled exceptions are correctly propagated through the application and its interface boundaries

Fault Tolerance, Security, Understandability

Guideline

It is important that exceptions that occur in application code are correctly handled or allowed to propagate through the application and its interface boundaries.  If code cannot recover from the exception then the exception should be allowed to propagate up the call stack.

Do not swallow exceptions (preventing them from propagating) if they cannot be properly handled as this can mask potential problems in the system.  Unhandled exceptions can be examined (if properly logged) so the application code can be improved to avoid similar problems in the future.

Unhandled system exceptions should be allowed to propagate to the top level or interface boundary of the application.  These should then be logged for analysis and troubleshooting.  However, system exceptions should not be allowed to propagate across application interface boundaries in their raw form as this can expose sensitive internal information such as stack traces, connection strings, user names and passwords, server names, database names, etc.  A custom application exception with a safe contextual error message should be passed back across the application boundary (after logging the original system exception).

Application exceptions that can be thrown by the application or class libraries form part of the API documentation and should be documented.

Do not pass exceptions via properties, return values, or out parameters.  This can cause confusing for other developers.

Requirement

Do not swallow exceptions with Catch statements that are empty or simply log the exception without propagating the exception.  If the exception can be handled and recovered from then the exception need not be logged.

Do not catch an exception and then set a property which the caller is then expected to check.

Do not catch an exception and then return the exception from a function or as an out parameter.

Do not provide methods that can either throw or not throw exceptions based on some option, e.g.

  • Function ParseUri(uriValue As String, throwOnError As Boolean) As Uri

Do not allow system exceptions to propagate across application interface boundaries in their raw form.  A custom application exception should be thrown instead to avoid exposing sensitive information.

System exceptions are to be logged at the application interface boundary or top-level of the application.

When re-throwing an exception (except at the application interface boundary), wrap the original exception (e.g. by setting the inner exception of the new exception - see standard When re-throwing a a caught exception use Throw and not Throw ex to preserve the stack trace).  This facilitates debugging exceptions as the original exception can be logged or examined.

Exemption

Private helper methods and exception building classes can be used for constructing and initialising exceptions in implementation code.

Checking Process

Check source code for Catch statements which are empty or simply log the exception and do nothing else.

Check that exceptions at the top-level of the application are logged and the application reports a safe contextual error to the user.

Check that system exceptions that can occur or propagate up to an interface boundary (e.g. a web service method) are logged and then re-thrown to the client as a custom application exception without exposing sensitive internal information.

Check that methods do not have a Boolean parameter that allows suppressing exceptions that may occur inside that method.

Check that methods do not return exception classes return values or out parameters.

Check that re-thrown exceptions wrap the original exception (except at application interface boundaries).

References

 

Back to top

33. Exceptions are properly logged before leaving an external interface boundary

Fault Tolerance

Guideline

Exceptions that arise at an application interface boundary such as a business services layer should be logged before propagating the exception or error message to the client.  This ensures that system and application errors that arise are properly logged with a sufficient level of information to allow diagnosing the cause of the error.

After logging the exception, a new exception should be created for propagating to the client/caller since returning an exception with detailed stack traces and other sensitive information can pose a security risk. A sanitised version of the exception should be returned to the client that complies with the interface contract.

Requirement

Exceptions are logged at interface boundaries before returning to the client.

A new sanitised version of the exception describing the cause of the error should be returned to the client, not the original exception (since this can exposure implementation details and other sensitive information).

System exceptions are never returned directly from the external interface boundary.

Checking Process

Identify the system boundaries/interfaces within the application code (these should be identified as system interfaces in the Context diagram of System Architecture Document and in Technical Design Document).

Check that any exceptions that may occur at the identified interfaces are logged before returning to the client.

Check that sanitised versions of the exceptions are returned (e.g. by constructing a new application exception) instead of the original exception.

Check that system exceptions are handled and not returned directly from the interface boundary.

References

 

Back to top

34. Exceptions are avoided by using validation methods when a validation method exists

Performance

Guideline

Where possible, exceptions should be avoided by first checking the state of an object or for any potential error conditions by using validation methods.

When designing your own classes, provide validation methods that allow checking for an erroneous condition (also called a "Tester-Doer" pattern) or return a Boolean value indicating whether an action succeeded for failed (also called the "TryParse" pattern after the TryParse methods used for parsing strings into .NET value types).

Examples of validation methods that can be used (for Visual Basic .NET) to avoid exceptions before they occur include:

  • File.Exists - Determines whether a file exists prior to performing an action on a filename.
  • Integer.TryParse, Double.TryParse, etc - Attempts to parse a string into another type and returns false if it failed.
  • Stream.CanRead - Returns true if a stream supports being read from.
  • StreamReader.EndOfStream - Checks if the current stream position is at the end of the stream.

Avoiding an exception is better than handling the exception as exception processing incurs performance penalties.

Requirement

Use defensive programming to anticipate exceptions that may occur by using validation methods (where they are available) and thus avoid incurring exception handling overheads.

Checking Process

Look for method calls to resources such as database connections, files, sockets, streams, parsers, etc.  If these classes provide a property or method for preventing an erroneous condition then it should be used rather than allowing the exception to occur in the event of an error.

References

 

Back to top

35. Data access is implemented in its own package to avoid repetitious data access code throughout the program

Modularity, Complexity, Understandability

Guideline

Database access in application code should be implemented in a separate package.  Typically, this is called the "Data Access Layer" and is a commonly featured component in a multi-layered architecture.

Placing data access code in its own package has the following benefits:

  • All data access code is located in one place which improves maintainability of the code.
  • Application code is simplified by using the data access package rather than having to solve the same problem many times.
  • Optimisations such as caching can be implemented in the data access package and are transparent to application code.
  • Data access code can potentially be reused in different applications.
  • Application code need not be tied to a specific database or data access implementation as the data access package encapsulates the implementation details.
  • Reduces coupling between object schema and data schema (if the data access package maps relational data to an object structure).

Requirement

All data access code is placed in its own separate package.

Exemption

Checking Process

Identify all source code that accesses a database.

Check whether this code accesses the database directly or via a data access layer.

Refer to Checking Process in All database access is done via stored procedures to identify .NET code accessing the database directly.

 

Back to top

36. Source control application folders should be maintained in the following manner

Understandability

Guideline

When creating a new application the folder structure should have the following structure. Future users will be able to easily identify between Source Code, Third Party Libraries, Releases, etc.

 

Application Path

Description

[Application Name]

Root application folder of source control.  This will be created by the Administrator.  This Application Folder will contain sub-project folders only.

[Application Name]\Source Code

Root directory of all source code.  This source code folder will contain sub-project folders and solution file only.

[Application Name]\Source Code\Solution Items

Solution Items (eg.3rd party libraries) can be created as required.

[Application Name]\Source Code\(Component Sub-Projects root)

Each component (client area) will have its own area within the Applications folder. For example [Application Name] may have two application [ComponentA] and [ComponentB]. In this scenario each component will have its own folder.

[Application Name]\Production

Root directory of all releases. Each release will have its own folder.

[Application Name]\Production\[Version_YYYYMMDD]

Root directory of each release. Make sure you include the config file from production.

Requirement

Checking Process

Check the application folder have above design.

References

 

Back to top

37. All code compiles cleanly without any warnings

Fault Tolerance

Guideline

It is a commonly recognised best practice to ensure production code compiles cleanly without any warnings.  Sometimes the compiler may report a warning which is actually a much more serious issue.  Some compilers allow using a setting to treat warnings as errors (e.g. Visual Studio .NET).

Requirement

  • Ensure code compiles in Release mode without any warnings before going into production.
  • If the compiler supports it, enable reporting of warnings as errors.

Checking Process

Compile the application:

  • Check that compilation succeeds and that there are no warnings reported.

References

 

Back to top

38. Code builds using relative paths and does not have hardcoded paths (e.g. to local developer folders)

Maintainability

Guideline

Code and associated project/solution files should not use hardcoded or absolute paths.  Code should compile cleanly without errors or warnings on a different machine to the one used for development.  If the source code is in a source code repository (such as Visual Source Safe), getting the latest source code and building it should work without altering paths in the source code or project/solution files.

Requirement

Source code should build on a different machine to the one used for development without requiring changes to paths in source code, project/solution files.

Checking Process

Build the code on a different machine to the one used for development - it should succeed without modifications to the solution/project files.

If the source code is in a source control repository, get the latest code and run a clean build to check that it succeeds.

References

 

Back to top

39. XML documentation is used for commenting all public, protected, and friend user-defined data types, subroutines, functions, and properties

Learnability

Guideline

XML documentation should be used for commenting all public, protected, and friend user-defined data types, subroutines, functions, and properties.

Visual Studio .NET has integrated support for XML documentation commenting, including Intellisense support.

Requirement

XML documentation commenting is used for all public, protected, and friend user-defined data types, subroutines, functions, and properties.

User-defined data types are commented as per standard Each user-defined data type has a description of its purpose.

Methods and Properties are commented as per standard Each method/property has a description of its purpose, parameters, returned values, and error conditions.

The recommended XML tags described in Recommended XML Tags for Documentation Comments (Visual Basic) are used for commenting.

Checking Process

Check a subset of the source code.

Ensure classes and other user-defined data types have XML documentation comments provided.

Ensure methods and properties have XML documentation comments provided.

 

Back to top

40. Modules are not used (use classes instead)

Understandability, Complexity

Guideline

Modules should not be used as they essentially make their methods, properties, and member variables global.  Methods, properties, and public member variables can be accessed from anywhere in the application code without having to use a fully qualified name.

Use static classes instead of modules.  These provide the same features but require qualifying the static methods and properties with the class name when used.

Do not use static classes or modules for holding public class members or public read/write properties (see standard Global variables are not used).

Requirement

  • Modules should not be used.
  • Static classes should be used instead of modules.
  • Static public class members or static public read/write properties are not used (see standard Global variables are not used).

Checking Process

Search all of the source code for the "Module" keyword.

References

 

Back to top

41. Custom application exceptions should inherit from Exception not SystemException

Understandability

Guideline

Custom application exceptions should not inherit from System.SystemExceptionSystem.SystemException is the base exception class used by the Common Language Runtime for all predefined system generated exceptions.

Application exceptions on the other hand are generated by the application code and as such should be distinguished from system exceptions.  Application exceptions should inherit from System.Exception.

The recommended approach is to create your own base application exception class which derives directly from System.Exception.

Requirement

Application exceptions do not inherit from System.SystemException.

Specific application exceptions inherit from a general base application exception defined for the application.

General base application exceptions inherit from System.Exception, not System.ApplicationException.

Checking Process

Search for all exception classes in the source code.

  • Custom application exception must not inherit from System.SystemException (search for "SystemException" to check)
  • Custom application exceptions should inherit from either System.Exception, System.ApplicationException or from a base application exception which inherits from ether System.Exception or System.ApplicationException.
 

Back to top

42. All disposable resources are acquired in a Using statement to guarantee disposal

Fault Tolerance

Guideline

All disposable resources should be acquired and used within a Using statement code block.  Disposable resources are those identified as classes which implement the IDisposable interface and as such expose a Dispose method.

Unmanaged resources are commonly implemented using the IDisposable interface.  These resources can include file handles, COM wrappers, and database connections.  Since these resources are not automatically cleaned up by the .NET Framework Garbage Collector it is important that they are explicitly disposed of as soon as they are no longer required so that the resources are properly returned to the system.

The Using statement guarantees disposal of the resources acquired (well, at least calling the Dispose method) when execution leaves the code block.  This is true even if an exception occurs within the Using state code block.

Note: The Using statement is equivalent of a Try...Finally block.

Requirement

All disposable resources such as database connections are instantiated with the Using statement.

Exemption

Unmanaged resources which are required for the lifetime of the containing class need not use the Using statement.  However, the containing class must implement the Dispose pattern to ensure the unmanaged resources are cleaned up when the class itself is disposed of (see Implementing Finalize and Dispose to Clean Up Unmanaged Resources).

Checking Process

Review code to ensure that all unmanaged resources are instantiated with the Using statement.

 

Back to top

43. Option Strict and Option Explicit are turned on for every Visual Basic .NET source code file

Fault Tolerance

Guideline

Each source code file should include Option Strict On and Option Explicit On at the top of the file. Whilst these can be turn on in the project settings, having them in each file makes it obvious that they are enabled.

Option Explicit On requires that you declare every variable in the file before you use it.  This helps avoid problems that can arise from misspelling variable names which could later result in a run-time error.  Option Explicit On enables these types of errors to be found at compile time.

Option Strict On disallows implicit narrowing type conversions, that is, converting a data type to another data type with less precision or smaller capacity (e.g. Double to Single).  Without Option Strict On, a run-time error would occur if the narrowing conversion failed.  Having the option enabled ensures compile-time notification for the developer so that these types of conversions can be avoided.  If the developer wants to proceed with the narrowing conversion they must use explicit conversion such as CType or DirectCast.

Option Strict On also generates errors for late binding - when a variable is declared to be of type Object.  This forces strong typing to detect potential type mismatch errors at compile-time and as an added bonus improves performance of code (compared to late binding).

Requirement

Each Visual Basic .NET source file contains the following two lines at the top:

Option Explicit On
Option Strict On

Checking Process

Check each Visual Basic source file has Option Explicit On and Option Strict On (see Requirement section).

References

 

Back to top

44. StringBuilder class is used for concatenating strings in loops

Performance

Guideline

When concatenating strings in a loop, the StringBuilder class should be used over normal String concatenation.

Strings are immutable in the .NET Framework so each concatenation discards the previous string and allocates memory for the new string.  Performing string concatenations in a loop can leave many objects for the Garbage Collector to clean up which reduces the performance of your application. Using StringBuilder avoids these problems.

Requirement

String concatenation in loops is performed with the StringBuilder class.

Do not concatenate strings using the & or + operators in loops.

Checking Process

Check code loops for evidence of string concatenation.  Ensure StringBuilder.Append is used for concatenation rather than the & or + operators.

References

 

Back to top

45. The .NET Framework Data Provider for SQL Server is used when connecting to SQL Server databases

Performance

Guideline

When using a SQL Server database (version 7.0 or later) the SQL Server Data Provider for the .NET Framework should be used.  This data provider is optimised for working with SQL Server databases and will perform the fastest.

Classes from the System.Data.SqlClient namespace make up the .NET Framework Data Provider for SQL Server.

Requirement

The SQL Server Data Provider must be used to connect to SQL Server databases V7.0 or later.

Checking Process

Check that all data access code uses the System.Data.SqlClient namespace classes for connecting to and manipulating the database.

If the Microsoft Data Access Application Block is used, check the application configuration file (Web.config or App.config) file to see that the data provider used is "System.Data.SqlClient":

<configuration>
  <configSections>
	<section name="dataConfiguration"
		  type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration
			 .DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data" />
  </configSections>
  <connectionStrings>
	<add
		name="Northwind"
		providerName="System.Data.SqlClient"
		connectionString="Data Source=(local);Initial
			Catalog=Northwind; Integrated Security=True" />
  </connectionStrings>
  <dataConfiguration defaultDatabase="Northwind"/>
</configuration>
	  
 

Back to top

46. Server-side threads are created from the .NET Thread Pool

Performance, Scalability

Guideline

Use the .NET Thread Pool when there is a need to perform an asynchronous action on the server.  Avoid creating new threads as there is a significant overhead in doing so and this reduces the performance of the server-side application.

In a client-server application, creating a new thread to service each user request limits the scalability of the application.

Requirement

Use the .NET Thread Pool for performing asynchronous actions on the server.

Do not create new thread instances, especially when servicing each user request.

Exemption

If the server side application requires a fixed number of threads to be created at start up which exist for the life-time of the application then the .NET Thread Pool should not be used.  Such situations might include a job processing thread that waits in the background for new jobs to be added to a queue.

Checking Process

Search source code for "New Thread" to identify places that threads have been created without the use of the thread pool.

References

 

Back to top

47. Use generic collections over the standard (System.Object-based) or specialized collection classes

Performance, Fault Tolerance

Guideline

Generic collections should be used as a first choice over the standard collections or specialised collection classes.

Generic collections perform better when storing value types (such as Integers, Structures, etc) since casting, boxing and unboxing of the value types to and from an object is avoided.  Iterating over a standard collection (such as an ArrayList) which holds value types can degrade performance significantly.

Specialized collection classes may take strongly type parameters rather than System.Object parameters but internally these are cast to and from System.Object as they are implemented using standard collections.

Another benefit of generic collections is strong typing which allows detecting type mismatches at compile-time.

Generic collection classes are located in the System.Collections.Generic namepace.

Requirement

Generic collections are used over the standard or specialized collection classes.

Exemption

If the .NET Framework, existing or third party code requires the use of non-generic collections then this is acceptable.  However, custom written code should use generic collections.

Checking Process

Check source code for use of standard collections:

  • Search for "System.Collections" namespace
  • Commonly used classes from this namespace include: ArrayList, Hashtable, Queue, Stack, SortedList

Check source code for use of specialized collections:

  • Search for "System.Collections.Specialized" namespace
  • Commonly used classes from this namespace include: NameValueCollection, StringCollection, StringDictionary, OrderedDictionary
 

Back to top

48. Do not omit code access levels (Public, Protected, Private, Friend)

Security, Understandability

Guideline

Code access levels (also called access modifiers) Public, Protected, Private, and Friend in Visual Basic should not be omitted from classes, interfaces, enumerations, properties, methods, member variables, etc.

When the access levels are omitted, the default is Public (except for class members where an access level or the Dim keyword is required).

For class member variables, do not use the Dim keyword use Private (even though it is equivalent to Private).

Explicitly stating the access level makes it clear to others what the intent is.  Using Private instead of Dim on class member variables is also clearer and consistent with other object-oriented language such as C#, Java, and C++.

Requirement

Access levels are not omitted from code (e.g. class, interfaces, enumerations, properties, methods, class member variables, constants, etc).

Checking Process

Check all user-defined types are prefixed with an access level.

Check all class member variables and constants are prefixed with an access level (this should be Private not Dim).

Check all class methods and properties are prefixed with an access level.

References

 

Back to top

49. String literals presented in user interface elements are not hardcoded - resource files are used

Maintainability

Guideline

Any string literals that are displayed to the user should not be hardcoded as literals or constants in the code.  The preferred method is to use resource files.

Using resource files allows you to change the data in the file without recompiling the entire application.  In addition to this benefit, satellite assemblies can be created containing localised resources for other cultures (if required).  These can be added to an application after it has already been deployed.

Requirement

Resource files are used for storing string literals presented to the user of the application.

Checking Process

Search for constant string literals used throughout the application:

  • Search for "Const " and check that the type is a String.
  • Search for quotation marks (") to identify any hardcoded string literals.
  • Check if this string constant is present to the user (e.g. in an error message or on a user interface control).

References

 

Back to top

50. Do not terminate or pause threads by calling Thread.Abort or Thread.Suspend, respectively

Fault Tolerance

Guideline

In a multi-threaded application do not terminate threads by calling Thread.Abort.  Aborting a thread in such a way causes the throwing of a ThreadAbortException without knowing what point the thread was up to in its processing.

An alternative is to use an EventWaitHandle such as the ManualResetEvent or AutoResetEvent to communicate between threads.  The main thread sets the EventWaitHandle and the processing thread then checks the status of the EventWaitHandle to see if it should terminate.

Similarly, do not call Thread.Suspend to pause a thread.  This method is marked as obsolete as of the .NET Framework 2.0.  An EventWaitHandle can be used to signal thread to tell it to wait on another WaitHandle which can be used to signal the thread to resume again.

Requirement

Thread.Abort is not to be used (see Guideline and References for alternative approaches).

Thread.Suspend is not to be used (see Guideline and References for alternative approaches).

Checking Process

Search the source code for ".Abort" and ".Suspend":

  • Check if these method calls are on a thread.
 

Back to top

51. When re-throwing a caught exception use Throw and not Throw ex to preserve the stack trace

Guideline

When catching and re-throwing an exception use Throw rather than Throw ex (where the exception variable is named ex).

Throw on its own retains the original call stack information where as Throw ex truncates the call stack information below the method that failed.

When throwing a new exception to provide more information, pass the original exception in the constructor to preserve the original exception and its stack trace (see References for further details).

Requirement

When catching and re-throwing an exception, use Throw on its own rather than Throw ex (where the exception variable is named ex).

When catching and re-throwing an exception with additional information, wrap the original exception in the new exception to preserve the original exception and its stack trace.

Exemption

At an application interface boundary, the original exception should be logged but not wrapped in the new exception (e.g. SoapException for a Web Service method) in order to hide sensitive implementation details (see standard Exceptions are properly logged before leaving an external interface boundary).

Checking Process

Search source code for use of Throw statements.

  • Check that the statement appears on its own if the original exception is being propagated.
  • If the original exception is being wrapped in a new exception, check that the original exception is passed into the constructor of the new exception.

References

 

Back to top