Painless Software Development Part 4

July 4th, 2010 by Kevin | No Comments | Filed in development
Please read the following first:
  1. Painless Software Development Part 1
  2. Painless Software Development Part 2
  3. Painless Software Development Part 3

In the final part of the series on Painless Software Development, I continue with the possible techniques that will help us to detect bugs early into the development before the code is committed to the main repository.

3) Assertions
One of the tools that we can use during the implementation to easily detect bugs caused by improper input arguments to the functions is the assert macro. Assert can be found in assert.h. The way to use assert is:

#include "assert.h"

void func(char* pString)
{
   assert (pString != NULL);
}

In this case, if pString turns out to be NULL when we pass it into the function func, in most compilers, assert will cause the debugger to stop at that particular point where the assertion has been placed indicating that the condition turned out to be false. Assert macro expands into code only in the debug version and in the release version it is replaced by nothing. So it helps in detecting potential bugs in the debug version while remaining completely invisible in the release version virtually placing no additional burden on the release version.

Assert macros should be used only for checking the entry points of functions and should not replaced error checking in the source code. Also care must be taken to ensure that adding assertions does not alter the execution of the software in any way i.e they should not alter the state of the variables, memory, files, etc so that the debug version and the release version of the software produce the same results.

4) Debugging
Bugs are introduced into the system either by writing new code or by modifications to the existing code. So whenever new code has been added or modifications have been made to the existing system, stepping through each of the new line of code while checking that it performs it’s intended purpose is a good way to ensure that are not adding logical bugs into the system.

Once the code is running and producing results that we expect, we tend to relax with a feeling that since the code is working there might not be bugs in it. However, there can be several issues that we do not consider such as a particular execution path is not being reached. Or that some of the variables may contain improper values perhaps even be working with garbage values. Debugging through your source code while checking the state of the variables and the path of execution will help to detect such kinds of situations which may turn into major bugs later in the development process.

A useful functionality of the debugger is the ability to change the values of variables during the execution. By altering the values of the variables we can check the different execution paths of the program thus increasing the coverage of our code and uncovering potential problems.

By stepping through your code, you could find the following types of bugs:

  • Overflow and underflow bugs
  • Data conversion bugs
  • Off-by-one bugs
  • NULL pointer bugs
  • Bugs using garbage memory
  • Assignment bugs in which you’ve used = instead of ==
  • Precedence bugs
  • Logic bugs

5) Code Reviews
Once the developer has implemented and tested the functionality of the source code and feels that it is complete, before finalizing the source code, it must be ensured that a thorough code review is performed for the new or modified code. The implementation should be explained to another developer with scope such as what was the requirement, how was it implemented, why a particular design was chosen, etc.

The reviewer on his part should examine the source code for possible discrepancies based on his experience. Viewing the source code with a different pair of eyes can can help recognize potential bugs that might not be recognized by the developer. A set of code review guidelines can be created to ensure that the quality of the source code remains unaffected by the new implementation or modifications to the source code.

 

Related Posts:

Tags:

Painless Software Development Part 3

July 3rd, 2010 by Kevin | No Comments | Filed in development

Before reading further, please read:
1) Painless Software Development Part 1
2) Painless Software Development Part 2

Development
Once the best design for the system has been chosen based on the requirements, it is time for the ideas to actually take physical form by the process of implementation or development. The implementation phase will also contain some designing such as the level of modularization that we need to achieve when we are implementing a function or a module. Following are some of the techniques to use during the development so that we can achieve a bug free implementation and be well on our way towards painless software development.

1) Compiler Warnings
When we use the compiler for development, many of us have a tendency to ignore the warnings by turning them off or setting them at a level that they can be ignored and we can still get the code compiled. However this is a way to hide many of the logical bugs that may be perfectly valid C idioms.
For example,

if(ch = '\t')
{
   //do something
}

The intention in this piece of code is to compare the character variable, ch, with the ‘\t’ tab character. Even though this is perfectly legal C code, it is a potential logical bug. In many compilers, a warning will be issued when an assignment statement is used in an if condition in such a way. However if the compiler is set to ignore warnings, then you will not be able to find this bug until possibly later in the development process.

Another example is the warning generated for type conversion. Assuming int has 2 bytes and you have a function,

void calculate(int a)
{
   //calculate an equation with a
}

unsigned int b = 65535;
calculate(b);

The implicit type conversion of the unsigned integer to a signed integer when it is passed into the function will generate a warning but if the compiler ignores such warnings then the result will not be what you expected.

The best way to ensure that such situations are detected and fixed as early as possible is to have the compiler report all warnings as errors so that you cannot have a compiled version of your code unless you have resolved all the warnings. Doing this can save you a lot of time as the compiler automatically detects potential bugs without you having to expend too much effort. Since they help detect potential bugs early in the development process, compiler warnings are a great help in the path towards painless software development.

2) Source Code Checker
A source code checker helps in detecting potential bugs in the system by checking for suspicious use of the programming language. Such a source code checker tool would have detected both of the sample compiler warnings shown in the earlier section. An example of a good source code checker is lint. Lint can help you detect some of the below mentioned problems:

  • Unused #include directives, variables, and procedures
  • Memory usage after its deallocation
  • Unused assignments
  • Usage of a variable value before its initialization
  • Deallocation of nonallocated memory
  • Unreached code
  • Implicit casts of actual arguments.
 

Related Posts:

Tags:

Painless Software Development Part 2

July 1st, 2010 by Kevin | No Comments | Filed in development

Before reading further, please read Painless Software Development Part 1

Continuing on our journey to achieve painless software development, we cover the next phase in the software development cycle which involves the designing of the system.

Design
The design is a critical phase of software development because the design chosen will have an impact on the software that will finally be shipped. That makes it an influential phase in the path towards painless software development. If the project is similar to earlier projects, then it is much easier to create a design based on the previously designed software systems. Experience of the earlier projects will help avoid the common pitfalls that were experienced earlier and that makes the implementation and maintenance of the new project that much easier. However if the project is a new one, it is of considerable importance to decide among various available designs and select the best one that will meet the requirement criteria of the project. In case of a maintenance project, the design process consists of looking into the existing implementation to try and see how the requirements could be implemented in the system and the impact of the changes on the existing system.

An issue that often creeps up during design and even during the implementation is significant change to the requirements. Ideally, the requirements should be frozen before the design and implementation. But in the real world, this is far from the situation. So the design should be flexible enough to be able to incorporate such changes in the requirements because you do not want a rigid design which will fall the moment there is a significant change in the requirements. Sure the design will have to be changed in such cases but we would want the changes to be as isolated as possible so that it will have as minimal an impact on the existing design and implementation as possible. That’s another key towards painless software development.

Estimation Level 1
As the design nears to completion and has reached up to a certain level, there also has to be an estimation that will cover the time required for the amount of work as mentioned in the design document. Estimation level 1 is a much more thorough estimation than level 0 because it attempts to look into how much time the actual implementation of the project will take place as per the design mentioned in the design document. It is quite difficult to estimate the work especially when it has to be done for the first time. However even if it is not possible to be accurate with the estimate, as much as possible it should try to cover how much time the major portion of the implementation will take. The amount of effort you put in to understand the amount of time required for the implementation will help you towards achieving painless software development.

 

Related Posts:

Tags: