Login(Email) Password Forget Password? Account Settings
Home ASP.net System Info C# Books Java Script Visual C++(MFC) C/C++ Win API Java Contact Us
Good Programming Practices
I start my discussion about this topic by  writing a program with two different approachs.
First Approach(Bad Approach) Second Approach(Good Approach)
#include <iostream.h> //Programmer: Mr. Ali
void main (void) //Dateof  Creation: 22/06/2007 
{ //Purpose: This program adds the prices of three items
along with the sales tax applied on total amount
int b[3]={100,200,300}; #include <iostream.h>
int a; void main (void)
for(int i=0;i<3;i++) {
a+=b[i];    const int SALE_TAX =15;
a+=a*15/100;    int saleTax=0
cout << a;    const int SIZE =3;
}     int price[SIZE]={100,200,300}; // array initialization
    int totalAmount=0;
    for(int i=0;i< SIZE ;i++)
         totalAmount=totalAmount+price[i]; // adding prices of items
    saleTax= totalAmount*SALE_TAX/100;   // calculating sale Tax
    totalAmount =totalAmount+saleTax;   // adding sale Tex to amount
    cout <<“The Amount Payable is= “ << totalAmount;
} // end of main
Comparison between above approaches
In First approach if somebody want to know what is the  purpose of this code. he/she has to read whole the code. where in second approach he/she will read the the purpose  at the top of the program. so it will save the time of the reader. so it is good practice to write few lines that will describe the program written below.
In First Approach. there is no indentation. so it difficult to read the program. In Second approach the program is properly indented. so it is very easy to read it. so it has very good readibility.
In First approach. the variables name are not more meaningful. but in second approach they are more meaningful. so it has good readibility.
In First approach, the array is declared as int b[3]; suppose later on we want to change the array size. we  have to change in loop also. but in second approach, for array index there is const int SIZE =3; now if we want to change the array size later on. we have to chnage in one place. So in this way we easily maintain our programs. So second approach saves our time.
In First approach there is no inline comments. but in second approach there are inline comments. so it is easy to understand the program. so easily maintainable.
In First approach, there are complex (more difficult to understand) statement. but in second approach statements are easy.
In first approach, the amount (output) will display without any message.but in second approach it will give proper message.
Questions that come to mind
Why do we use these good programming approach?
What kind of problems can occur without using these good programming approach?
Let start the SDLC(Software Development Life Cycle )
  • Requirements Specifications (Analysis)
  • Design
  • Implementation
  • Integration
  • Testing
  • Deployment (installment of the software on the client site.)
  • Maintenance Software maintenance phase involves changes to the software in order to correct defects and deficiencies found during field usage as well as addition of new functionality to improve the software’s usability and applicability
Note: To easy maintain your program you have to write your code with good programming approach.
Maintainability
  • Maintainability is the most desirable quality of a software artifact.
  • Good software ought to have code that is easy to maintain.
  • It is not important to write code that works, it is important to write code that works and is easy to understand so that it can be maintained.
  • The three basic principles that guide maintainability are:
    •  simplicity
    • clarity
    • generality or flexibility

Simplicity and clarity help in making the code easier to understand,Flexibility facilitates easy enhancement of the software.

Self Documenting Code
  • Self-documenting code is that code which explains itself without the need of comments and extraneous documentation, like flowcharts, UML diagrams, process-flow state diagrams, etc.
  •  The question is: how can we write code that is self-documenting?
  • There are a number of attributes that contributes towards making the program self documented.
  • These include:
    •  The size of each function
    • Choice of variables and other identifier names
    • Style of writing expressions
    • Structure of programming statements Comments
    • Modularity and issues relating to performance and portability.
Function Size
  • The size of individual functions plays a significant role in making the program easy or difficult to understand.
  • In general, as the function becomes longer in size, it becomes more difficult to understand.
  • A function should not be larger than 20 lines of code and in any case should not exceed one page in length.
Modularity
  • Abstraction and Encapsulation are two important tools that can help in managing and mastering the complexity of a program.
  •  Modularity is a tool that can help us in reducing the size of individual functions, making them more readable.
  • As an example, consider the following selection sort function:
Example Selection sort without Modularity
void selectionSort(int a[], int size)
{
   int i, j, temp, min;
   for (i = 0; i < size-1; i++)
  {
     min = i;
     for (j = i+1; j < size; j++)
      {
        if (a[j] < a[min])
       min = j;
     } // end of inner for loop
    temp = a[i];
    a[i] = a[min];
    a[min] = temp;
   }// end of outer for loop
}// end of selectionsort function
Example Selection sort with Modularity

int minimum(int a[], int from, int to)
{
   int i, min; 
   min = a[from];
   for (i = from; i <= to; i++)
   {
     if (a[i] < a[min])
     min = i;
   }// end of for loop 
  return min; // end of minimum function
}

void swap(int &x, int &y)
{
   int temp;
   temp = x;
   x = y;
   y = temp;
}

void selectionSort(int a[], int size)
{
  int min, i;
  for (i = 0; i < size; i++)
   {
     min = minimum(a, i, size –1);  // function call
     swap(a[i], a[min]); // function call
  }
}

  • Reusability is one of the prime reasons to make functions but is not the only reason.
  • Modularity is of equal concern (if not more) and a function should be broken into smaller pieces, even if those pieces are not reused.
Identifier Names
Identifier names also play a significant role in enhancing the readability of a program.
Example
  • if (x==0) // this is the case when we are allocating a new number In this particular case, the meanings of the condition in the if-statement are not clear and we had to write a comment to explain it. This can be improved if instead of using x, we use a more meaningful name. Our new code becomes:
  •  if (AllocFlag == 0) The situation has improved a little bit but the semantics of the condition are still not very clear, as the meaning of 0 is not very clear. Now consider the following statement:
  •  If (AllocFlag == NEW_NUMBER) We have improved the quality of the code by replacing the number 0 with a named constant NEW_NUMBER. Now, the semantics are clear and do not need any extra comments, hence this piece of code is self-documenting.
Coding Style Guide
  • Consistency plays a very important role in making code self-documenting. A consistently written code is easier to understand and follow.
  •  A coding style guide is aimed at improving the coding process and to implement the concept of standardized and relatively uniform code throughout the application or project.
  • As a number of programmers participate in developing a large piece of code, it is important that a consistent style is adopted and used by all. Therefore, each organization should develop a style guide to be adopted by its entire team.
Naming Conventions
  • Charles Simonyi of Microsoft first discussed the Hungarian Notation. It is a variable naming convention that includes information about the variable in its name (such as data type, whether it is a reference variable or a constant variable, etc). int ilength, float fprice
  •  Every company and programmer seems to have his or her own flavor of Hungarian Notation.
  •  The advantage of Hungarian notation is that just by looking at the variable name, one gets all the information needed about that variable.
General Naming Conventions
  • Names representing types must be nouns and written in mixed case starting with upper case. Circle, FilePrefix
  • Variable names must be in mixed case starting with lower case. circle, filePrefix
  • This makes variables easy to distinguish from types, and effectively resolves potential naming collision as in the declaration Circle circle;
  • Names representing constants must be all uppercase using underscore to separate words. MAX_ITERATIONS, COLOR_RED In general, the use of such constants should be minimized. In many cases implementing the value as a method is a better choice. This form is both easier to read, and it ensures a uniform interface towards class values.
      int getMaxIterations()// NOT: MAX_ITERATIONS = 25
         {
         return 25;
         }
  • Names representing methods and functions should be verbs and written in mixed case starting with lower case. getName(), computeTotalWidth()
  • Names representing template types in C++ should be a single uppercase letter. template < class  T > ...
    template <  class C,class D>.......
  • Private class variables should have _ suffix.
    class SomeClass
    {
    private int length_;
    ...
    }
    Apart from its name and its type, the scope of a variable is its most important feature. Indicating class scope by using _ makes it easy to distinguish class variables from local scratch variables 
  • Abbreviations should not be uppercase when used as name. exportHtmlSource(); // NOT: exportHTMLSource(); openDvdPlayer(); // NOT: openDVDPlayer();
  • Variables with a large scope should have long names; variables with a small scope can have short names. Scratch variables used for temporary storage or indices are best kept short. A programmer reading such variables should be able to assume that its value is not used outside a few lines of code. Common scratch variables for integers are i, j, k, m, n and for characters c and d.
  • The name of the object is implicit, and should be avoided in a method name. line.getLength(); // NOT: line.getLineLength(); The latter seems natural in the class declaration, but proves superfluous in use.
  •  The terms get/set must be used where an attribute is accessed directly.
    employee.getName();
    matrix.getElement (2, 4);
    employee.setName (name);
    matrix.setElement (2, 4, value);
  • is prefix should be used for boolean variables and methods.
    isSet, isVisible, isFinished, isFound, isOpen
    Using the is prefix solves a common problem of choosing bad Boolean names like status or flag. isStatus or isFlag simply doesn't fit, and the programmer is forced to chose more meaningful names. There are a few alternatives to the is prefix that fits better in some situations. These are has, can and should prefixes:
    boolean hasLicense();
    boolean canEvaluate();
    boolean shouldAbort = false;
  • The term compute can be used in methods where something is computed.
    valueSet.computeAverage();
    matrix.computeInverse()
    Using this term will give the reader immediate clue that this is a potential time consuming operation
  • The term find can be used in methods where something is looked up.
    vertex.findNearestVertex();
    matrix.findMinElement();
    This tells the reader that this is a simple look up method with a minimum of computations involved.
  • The term initialize can be used where an object or a concept is established.
    printer.initializeFontSet();
  • List suffix can be used on names representing a list of objects.
    vertex (one vertex), vertexList (a list ofvertices)
    A list in this context is the compound data type that can be traversed backwards, forwards, etc. (typically a Vector).
  •  n prefix should be used for variables representing a number of objects.
    nPoints, nLines
    The notation is taken from mathematics where it is an established convention for indicating a number of objects.
  • Iterator variables should be called i, j, k etc .
    while(Iterator i = pointList.iterator();i.hasNext();)
    {
    }
    for (int i = 0; i < nTables; i++)
    {
    }
     The notation is taken from mathematics where it is an established convention for indicating iterators. Variables named j, k etc. should be used for nested loops only.
  •  Complement names must be used for complement entities. get/set, add/remove, create/destroy, start/stop, insert/delete, increment/decrement, old/new, begin/end, first/last, up/down, min/max, Reduce complexity by symmetry.
  • Abbreviations in names should be avoided.
    computeAverage(); // NOT: compAvg();
    There are two types of words to consider. First are the common words listed in a language dictionary, these must never be abbreviated. Never write:
    cmd instead of command
    cp instead of copy
    pt instead of point
    comp instead of compute
    init instead of initialize

    etc. Then there are domain specific phrases that are more naturally known through their acronym or abbreviations. These phrases should be kept abbreviated. Never write:
    HypertextMarkupLanguage instead of html
    CentralProcessingUnit instead of cpu
    PriceEarningRatio instead of pe
  • Negated Boolean variable names must be avoided.
    boolean isError; // NOT: isNotError
    boolean isFound; // NOT: isNotFound
     The problem arise when    the logical NOT operator is used and double negative arises. It is not immediately apparent what !isNotError means.
Variables
  • Variables should be initialized where they are declared and they should be declared in the smallest scope possible.
  • Variables must never have dual meaning. This enhances readability by ensuring all concepts are represented uniquely. Reduce the chance of error by side effects.
  • Variables should be kept alive for as short a time as possible. Keeping the operations on a variable within a small scope, it is easier to control the effects and side effects of the variable.
  • Class variables should never be declared public. The concept of information hiding and encapsulation is violated by public variables. Use private variables and access functions instead. One exception to this rule is when the class is essentially a data structure, with no behavior (equivalent to a C++ struct). In this case it is appropriate to make the class’ instance variables public.
  • Related variables of the same type can be declared in a common statement. Unrelated variables should not be declared in the same statement.
    float x, y, z;
    float revenueJanuary,revenueFebrury,revenueMarch;

    The common requirement of having declarations on separate lines is not useful in the situations like the ones above. It enhances readability to group variables.
  •  Global variables should not be used. Variables should be declared only within the scope of their use. The same is recommended for global functions or file scope variables. It is easier to control the effects and side effects of the variables if used in limited scope.
  • Implicit test for 0 should not be used other than for Boolean variables and pointers.
    if (nLines != 0) // NOT: if (nLines)
    if (value != 0.0) // NOT: if (value)
    It is not necessarily defined by the compiler that ints and floats 0 are implemented as binary 0. Also, by using explicit test the statement give immediate clue of the type being tested. It is common also to suggest that pointers shouldn't test implicit for 0 either,
    i.e. if (line == 0) instead of if (line).
    The latter is regarded as such a common practice in C/C++ however that it can be used.
Loop Structures

Only loop control statements must be included in the for() construction. int sum = 0;
for (i=0; i<100; i++)
sum = sum+value[i];
// NOT: // for (i=0, sum=0; i<100; i++)
// sum +=value[i];
Loop variables should be initialized immediately before the loop.
boolean done = false;
while (!done)
{
..
}
// NOT: // boolean done =false;
//....... .  //more statements are initilization of the variables
while (!done)
{
//....
}

  • The use of break and continue in loops should be avoided. These statements should only be used if they prove to give higher readability than their structured counterparts. In general break should only be used in case statements and continue should be avoided altogether
Conditionals

Complex conditional expressions must be avoided. Introduce temporary Boolean variables instead.
if((elementNo < 0) || (elementNo > maxElement)|| elementNo == lastElement)
{
:
}
The above statement should be replaced by:
boolean isFinished = (elementNo < 0) || (elementNo > maxElement);< /STRONG >
boolean isRepeatedEntry = elementNo = = lastElement;
if (isFinished || isRepeatedEntry)
{
:
}

  • The nominal case should be put in the if-part and the exception in the else- part of an if statement.
    boolean isError = readFile (fileName);
    if (!isError)
    {
    :
    }
    else
    {
    :
    }

    The conditional should be put on a separate line.
    if (isDone) // NOT: if (isDone) doCleanup();
    doCleanup();
  • Executable statements in conditionals must be avoided.
    file = openFile (fileName, "w");
    if (file != null)
    {
    :
    }
    // NOT: // if((file = openFile (fileName, "w")) != null)
    {
    //
    :
    //
    }
Short circuiting || and &

Short-circuiting is a very useful tool. It can be used where one Boolean expression can be placed first to “guard” a potentially unsafe operation in a second Boolean expression. Also, time is saved in evaluation of complex expressions using operators || and &&. However, a number of issues arise if proper attention is not paid. Let us look at the following code segment taken from commercially developed software for a large international bank:
struct Node
{
int data;
Node * next;
};
Node *ptr;
...
while (ptr->data < myData && ptr != NULL)
{
// do something here
}

What is wrong with this code?

The second part of condition, ptr != NULL, is supposed to be the guard. That is, if the value of the pointer is NULL, then the control should not enter the body of the while loop otherwise, it should check whether ptr->data < myData or not and then proceed accordingly. When the guard is misplaced, if the pointer is NULL then the program will crash because it is illegal to access a component of a non-existent object. This code is rewritten as follows. This time the short-circuiting helps in achieving the desired objective, which would have been a little difficult to code without such help.
while (ptr != NULL && ptr->data< myData)
{
// do something here
}

Operand Evaluation Order and Side Effects

A side effect of a function occurs when the function, besides returning a value, changes either one of its parameters or a variable declared outside the function that is accessible to it. That is, a side effect is caused by an operation that may return an explicit result but it may also modify the values stored in other data objects. Side effects are a major source of programming errors and they make things difficult during maintenance or debugging activities. Many languages do not specify the function evaluation order in a single statement. This combined with side effects causes major problems. As an example, consider the following statement:
c = f1(a) + f2(b);

Which function (f1 or f2) will be evaluated first, as the C/C++ language does not specify the evaluation order and the implementer (compiler writer) is free to choose one order or the other? The question is: does it matter?

To understand this, let’s look at the definition of f1 and f2:
int f1(int &x)
{
x = x * 2;
return x + 1;
}
int f2(int &y)
{
y = y / 2;
return x – 1;
}

In this case both f1 and f2 have side effects as they both are doing two things - changing the value of the parameter and changing the value at the caller side. Now if we have the following code segment:
a = 3;
b = 4;
c = f1(a) + f2(b);
Then the value of a, b, and c will be:
a = 6
b = 2
c = 8
So far there doesn’t seem to be any problem. But let us now consider the following statement:
c = f1(a) + f2(a);

What will be the value of a and c after this statement? If f1 is evaluated before f2, then we have the following values:
a = 3
b = 9 // 7 + 2
On the other hand, if f2 is evaluated before f2 then, we get totally different results:
a = 2
b = 3 // 3 + 0

Common Mistakes

Following is the short list of common mistakes made due to side effects:
1. array[i++] = i; If i is initially 3, array[3] might be set to 3 or 4.
2. array[i++] = array[i++] = x; Due to side effects, multiple assignments become very dangerous. In this example, a whole lot depends upon when i is incremented.

3. “,” is very dangerous as it causes side effects. Let’s look at the following statement:
int i, j = 0;
Because of the syntax, many people would assume that i is also being initialized to 0, while it is not. Combination of , and = -- is fatal. Look at the following statement:
int a = b, c = 0;
A majority of the programmers would assume that all a, b, and c are being initialized to 0 while only c is initialized and a and b have garbage values in them. This kind of negligence causes major programming errors that are not caught easily and are caused only because there are side effects.