Software Engineering
Software Engineering
The way error conditions are reported by different functions in a program should be
standard within an organisation.
For example, all functions while encountering an error condition should either return a 0
or 1.
This facilitates reuse and debugging.
Representative coding guidelines:
The following are some representative coding guidelines that are recommended by many
software development organisations.
Wherever necessary, the rationale behind these guidelines is also mentioned.
Do not use a coding style that is too clever or too difficult to understand:
Code should be easy to understand.
Many inexperienced engineers actually take pride in writing cryptic and
incomprehensible code.
Clever coding can obscure meaning of the code and reduce code understandability;
thereby making maintenance and debugging difficult and expensive.
Avoid obscure side effects:
The side effects of a function call include modifications to the parameters passed by
reference, modification of global variables, and I/O operations.
An obscure side effect is one that is not obvious from a casual examination of the
code.
Obscure side effects make it difficult to understand a piece of code.
For example, suppose the value of a global variable is changed or some file I/O is
performed obscurely in a called module.
That is, this is difficult to infer from the function’s name and header information.
Then, it would be really hard to understand the code.
Do not use an identifier for multiple purposes:
Programmers often use the same identifier to denote several temporary entities. For
example, some programmers make use of a temporary loop variable for also
computing and storing the final result.
The rationale that they give for such multiple use of variables is memory efficiency,
e.g., three variables use up three memory locations, whereas when the same variable
is used for three different
purposes, only one memory location is used. However, there are several things
wrong with this approach and hence should be avoided.
Some of the problems caused by the use of a variable for multiple purposes are as
follows:
Each variable should be given a descriptive name indicating its purpose.
This is not possible if an identifier is used for multiple purposes.
Use of a variable for multiple purposes can lead to confusion and make it
difficult for somebody trying to read and understand the code.
Use of variables for multiple purposes usually makes future enhancements more
difficult.
For example, while changing the final computed result from integer to float type,
the programmer might subsequently notice that it has also been used as a
temporary loop variable that cannot be a float type.
Code should be well-documented:
As a rule of thumb, there should be at least one comment line on the average for
every three source lines of code.
Length of any function should not exceed 10 source lines: A lengthy function is
usually very difficult to understand as it probably has a large number of variables and
carries out many different types of computations.
SE -u5 3 of 10
For the same reason, lengthy functions are likely to have disproportionately larger
number of bugs.
Do not use GO TO statements: Use of GO TO statements makes a program
unstructured. This makes the program very difficult to understand, debug, and maintain.
CODE REVIEW
Review is a very effective technique to remove defects from source code.
Code review for a module is undertaken after the module successfully compiles.
That is, all the syntax errors have been eliminated from the module.
Obviously, code review does not target to design syntax errors in a program, but is designed
to detect logical, algorithmic, and programming errors.
Code review has been recognised as an extremely cost-effective and for producing high
quality code.
Normally, the following two types of reviews are carried out on the code of a module:
Code walkthrough.
Code inspection.
Clean Room Testing
Code walkthrough:
Code walkthrough is an informal code analysis technique.
A few members of the development team are given the code a couple of days before the
walkthrough meeting.
Each member selects some test cases and simulates execution of the code by hand
The main objective of code walkthrough is to discover the algorithmic and logical errors
in the code.
The members note down their findings of their walkthrough and discuss those in a
walkthrough meeting where the coder of the module is present.
Even though code walkthrough is an informal analysis technique, several guidelines
have evolved over the years for making this naive but useful analysis technique more
effective.
Some of these guidelines are following:
The team performing code walkthrough should not be either too big or too small.
Ideally, it should consist of between three to seven members.
Discussions should focus on discovery of errors and avoid deliberations on how to
fix the discovered errors.
In order to foster co-operation and to avoid the feeling among the engineers that they
are being watched and evaluated in the code walkthrough meetings, managers should
not attend the walkthrough meetings.
Code Inspection
During code inspection, the code is examined for the presence of some common
programming errors.
This is in contrast to the hand simulation of code execution carried out during code
walkthroughs.
We can state the principal aim of the code inspection to be the following:
Check for the presence of some common types of errors that usually creep into code
due to programmer mistakes and oversights and
Check whether coding standards have been adhered to.
Following is a list of some classical programming errors which can be checked during
code inspection:
Use of uninitialised variables.
Jumps into loops.
Non-terminating loops.
Incompatible assignments.
SE -u5 4 of 10
As can be seen, the test cases are first designed, the test cases are run to detect
failures. The bugs causing the failure are identified through debugging, and the
identified error is corrected. Of all the above mentioned testing activities, debugging
often turns out to be the most time-consuming activity.
Types of testing:
Unit testing
Black-box approach
White-box (or glass-box) approach
Integration testing
System testing
Unit testing
SE -u5 6 of 10
During unit testing, the individual functions (or units) of a program are tested.
Unit testing is undertaken after a module has been coded and reviewed.
This activity is typically undertaken by the coder of the module himself in the coding
phase. Before carrying out unit testing, the unit test cases have to be designed and the
test environment for the unit under test has to be developed.
Driver and stub modules
In order to test a single module, we need a complete environment to provide all
relevant code that is necessary for execution of the module.
The procedures belonging to other modules that the module under test calls.
Non-local data structures that the module accesses.
A procedure to call the functions of the module under test with appropriate
parameters.
Stub: The role of stub and driver modules is pictorially shown in the below figure
A stub procedure is a dummy procedure that has the same I/O parameters as the
function called by the unit under test but has a highly simplified behaviour. For
example, a stub procedure may produce the expected behaviour using a simple table
look up mechanism.
BLACK-BOX TESTING
In black-box testing, test cases are designed from an examination of the input/output
values only and no knowledge of design or code is required.
The following are the two main approaches available to design black box test cases:
Equivalence class partitioning
Boundary value analysis
Equivalence class partitioning
In the equivalence class partitioning approach, the domain of input values to the
program under test is partitioned into a set of equivalence classes.
The partitioning is done such that for every input data belonging to the same
equivalence class, the program behaves similarly.
General guidelines for designing the equivalence classes:
If the input data values to a system can be specified by a range of values, then one
valid and two invalid equivalence classes need to be defined.
If the input data assumes values from a set of discrete members of some domain,
then one equivalence class for the valid input values and another equivalence class
for the invalid input values should be defined.
SE -u5 7 of 10
A test suite achieves path coverage if it exeutes each linearly independent paths
( o r basis paths ) at least once. A linearly independent path can be defined in
terms of the control flow graph (CFG) of a program.
Control flow graph (CFG)
A control flow graph describes how the control flows through the program.
In this approach, once a failure is observed, the symptoms of the failure (i.e.,
certain variable is having a negative value though it should be positive, etc.) are
noted.
Based on the failure symptoms, the causes which could possibly have contributed
to the symptom is developed and tests are conducted to eliminate each.
Program slicing
This technique is similar to back tracking. In the backtracking approach, one
often has to examine a large number of statements. However, the search space is
reduced by defining slices. A slice of a program for a particular variable and at a
particular statement is the set of source lines preceding this statement that can
influence the value of that variable.
Debugging Guidelines
Many times debugging requires a thorough understanding of the program design.
Trying to debug based on a partial understanding of the program design may require an
inordinate amount of effort to be put into debugging even for simple problems.
Debugging may sometimes even require full redesign of the system. In such cases, a
common mistakes that novice programmers often make is attempting not to fix the error
but its symptoms.
One must be beware of the possibility that an error correction may introduce new errors.
Therefore after every round of error-fixing, regression testing must be carried out.
PROGRAM ANALYSIS TOOLS
A program analysis tool usually is an automated tool that takes either the source code or the
executable code of a program as input and produces reports regarding several important
characteristics of the program, such as its size, complexity, adequacy of commenting,
adherence to programming standards, adequacy of testing, etc.
We can classify various program analysis tools into the following two broad
categories:
Static analysis tools
Dynamic analysis tools
Static Analysis Tools
Static program analysis tools assess and compute various characteristics of a program
without executing it. Typically, static analysis tools analyse the source code to compute
certain metrics characterising the source code (such as size, cyclomatic complexity, etc.)
and also report certain analytical conclusions.
In this context, it displays the following analysis results:
To what extent the coding standards have been adhered to?
Whether certain programming errors such as uninitialised variables, mismatch
between actual and formal parameters, variables that are declared but never used,
etc., exist?
A list of all such errors is displayed.
Dynamic Analysis Tools
Dynamic program analysis tools can be used to evaluate several program characteristics
based on an analysis of the run time behaviour of a program.
These tools usually record and analyse the actual behaviour of a program while it is
being executed.
A dynamic program analysis tool (also called a dynamic analyser ) usually collects
execution trace information by instrumenting the code.