Best coding practices are a set of informal rules that
the software development community has learned over time which can help
improve the quality of software.
Many computer programs remain in use for far longer than the original
authors ever envisaged (sometimes 40 years or more), so any rules need
to facilitate both initial development and subsequent maintenance and
enhancement by people other than the original authors.
In Ninety-ninety rule, Tim Cargill is credited with this explanation as
to why programming projects often run late: "The first 90% of the code
accounts for the first 90% of the development time. The remaining 10% of
the code accounts for the other 90% of the development time." Any
guidance which can redress this lack of foresight is worth considering.
The size of a project or program has a significant effect on error
rates, programmer productivity, and the amount of management needed.
Software quality
As listed below,
there are many attributes associated with good software. Some of these
can be mutually contradictory (e.g. very fast versus full error
checking), and different customers and participants may have different
priorities. Weinberg provides an example of how different goals can have
a dramatic effect on both effort required and efficiency. Furthermore,
he notes that programmers will generally aim to achieve any explicit
goals which may be set, probably at the expense of any other quality
attributes.
Sommerville has identified four generalised attributes which are not
concerned with what a program does, but how well the program does it:
- Maintainability.
- Dependability.
- Efficiency.
- Usability.
Weinberg has identified four targets which a good program should meet:
- Does a program meet its specification; "correct output for each possible input"?
- Is the program produced on schedule (and within budget)?
- How adaptable is the program to cope with changing requirements?
- Is the program efficient enough for the environment in which it is used?
Hoare has identified seventeen objectives related to software quality, including:
- Clear definition of purpose.
- Simplicity of use.
- Ruggedness (difficult to misuse, kind to errors).
- Early availability (delivered on time when needed).
- Reliability.
- Extensibility in the light of experience.
- Brevity.
- Efficiency (fast enough for the purpose to which it is put).
- Minimum cost to develop.
- Conformity to any relevant standards.
- Clear, accurate, and precise user documents.
Prerequisites
Before coding starts, it is important to ensure that all necessary
prerequisites have been completed (or have at least progressed far
enough to provide a solid foundation for coding). If the various
prerequisites are not satisfied then the software is likely to be
unsatisfactory, even if it is completed.
From Meek & Heath: "What happens before one gets to the coding stage
is often of crucial importance to the success of the project."
The prerequisites outlined below cover such matters as:
- how is development structured? (life cycle)
- what is the software meant to do? (requirements)
- the overall structure of the software system (architecture)
- more detailed design of individual components (design)
- choice of programming language(s)
For small simple projects involving only one person, it may be feasible
to combine architecture with design and adopt a very simple life cycle.
Life cycle
A software development methodology is a framework that is used to
structure, plan, and control the life cycle of a software product.
Common methodologies include waterfall,prototyping, iterative and
incremental development, spiral development, agile software
development, rapid application development, and extreme programming.
The waterfall model is a sequential development approach; in particular,
it assumes that the requirements can be completely defined at the start
of a project. However, McConnell quotes three studies which indicate
that, on average, requirements change by around 25% during a project.[9] The
other methodologies mentioned above all attempt to reduce the impact of
such requirement changes, often by some form of step-wise, incremental,
or iterative approach. Different methodologies may be appropriate for
different development environments.
Requirements
McConnell states: "The first prerequisite you need to fulfill before
beginning construction is a clear statement of the problem the system is
supposed to solve.
Meek and Heath emphasise that a clear, complete, precise, and
unambiguous written specification is the target to aim for. Note that it
may not be possible to achieve this target, and the target is likely to
change anyway (as mentioned in the previous section).
Sommerville distinguishes between less detailed user requirements and
more detailed system requirements. He also distinguishes between
functional requirements (e.g. update a record) and non-functional
requirements (e.g. response time must be less than 1 second).
Architecture
Hoare points out: “there are two ways of constructing a software design: one way is to make it so simple that there are obviously no deficiencies; the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.
Software architecture is concerned with deciding what has to be done,
and which program component is going to do it (how something is done is
left to the detailed design phase, below). This is particularly
important when a software system contains more than one program, since
it effectively defines the interface between these various programs. It
should include some consideration of any user interfaces as well,
without going into excessive detail.
Any non-functional system requirements (response time, reliability, maintainability, etc.) need to be considered at this stage.
The software architecture is also of interest to various stakeholders
(sponsors, end-users, etc.) since it gives them a chance to check that
their requirements can be met.
Design
The main purpose of design is to fill in the details which have been
glossed over in the architectural design. The intention is that the
design should be detailed enough to provide a good guide for actual
coding, including details of any particular algorithms to be used. For
example, at the architectural level it may have been noted that some
data has to be sorted, while at the design level it is necessary to
decide which sorting algorithm is to be used. As a further example, if
an object-oriented approach is being used, then the details of the
objects must be determined (attributes and methods).
Choice of programming language(s)
Mayer states: “No programming language is perfect. There is not even a
single best language; there are only languages well suited or perhaps
poorly suited for particular purposes. Understanding the problem and
associated programming requirements is necessary for choosing the
language best suited for the solution.
From Meek & Heath: "The essence of the art of choosing a language is
to start with the problem, decide what its requirements are, and their
relative importance, since it will probably be impossible to satisfy
them all equally well. The available languages should then be measured
against the list of requirements, and the most suitable (or least
unsatisfactory) chosen.
It is possible that different programming languages may be appropriate
for different aspects of the problem. If the languages or their
compilers permit, it may be feasible to mix routines written in
different languages within the same program.
Even if there is no choice as to which programming language is to be
used, McConnell provides some advice: "Every programming language has
strengths and weaknesses. Be aware of the specific strengths and
weaknesses of the language you're using.
Coding standards
This section is also really a prerequisite to coding, as McConnell
points out: "Establish programming conventions before you begin
programming. It's nearly impossible to change code to match them later.
As listed near the end of Coding conventions, there are different
conventions for different programming languages, so it may be
counterproductive to apply the same conventions across different
languages.
The use of coding conventions is particularly important when a project
involves more than one programmer (there have been projects with
thousands of programmers). It is much easier for a programmer to read
code written by someone else if all code follows the same conventions.
For some examples of bad coding conventions, Roedy Green provides a
lengthy (tongue-in-cheek) article on how to produce unmaintainable code.
Commenting
Due to time restrictions or enthusiastic programmers who want immediate
results for their code, commenting of code often takes a back seat.
Programmers working as a team have found it better to leave comments
behind since coding usually follows cycles, or more than one person may
work on a particular module. However, some commenting can decrease the
cost of knowledge transfer between developers working on the same
module.
In the early days of computing, one commenting practice was to leave a brief description of the following:
- Name of the module.
- Purpose of the Module.
- Description of the Module (In brief).
- Original Author
- Modifications
- Authors who modified code with a description on why it was modified.
However, the last two items have largely been obsoleted by the advent of revision control systems.
Also regarding complicated logic being used, it is a good practice to
leave a comment "block" so that another programmer can understand what
exactly is happening.
Unit testing can be another way to show how code is intended to be used.
Modifications and authorship can be reliably tracked using a
source-code revision control system, rather than using comments.
Naming conventions
Use of proper naming conventions is considered good practice. Sometimes
programmers tend to use X1, Y1, etc. as variables and forget to replace
them with meaningful ones, causing confusion.
In order to prevent this waste of time, it is usually considered good
practice to use descriptive names in the code since we deal with real
data.
Example: A variable for taking in weight as a parameter for a truck can
be named TrkWeight or TruckWeightKilograms, with TruckWeightKilograms
being the more preferable one, since it is instantly recognisable.
See CamelCase naming of variables.
Keep the code simple
The code that a programmer writes should be simple. Complicated logic
for achieving a simple thing should be kept to a minimum since the code
might be modified by another programmer in the future. The logic one
programmer implemented may not make perfect sense to another. So, always
keep the code as simple as possible.
Portability
Program code should never ever contain "hard-coded" (literal)
values referring to environmental parameters, such as absolute file
paths, file names, user names, host names, IP addresses, URLs, UDP/TCP
ports. Otherwise the application will not run on a host that has a
different design than anticipated. A careful programmer can parametrize
such variables and configure them for the hosting environment outside of
the application proper (for example in property files, on an
application server, or even in a database). Compare the mantra of a
"single point of definition" (SPOD).
As an extension, resources such as XML files should also contain
variables rather than literal values, otherwise the application will not
be portable to another environment without editing the XML files. For
example, with J2EE applications running in an application server, such
environmental parameters can be defined in the scope of the JVM and the
application should get the values from there.
Code development
Code building
A best practice for building code involves daily builds and testing, or
better still continuous integration, or even continuous delivery.
Testing
Testing is an integral part of software development that needs to be
planned. It is also important that testing is done proactively; meaning
that test cases are planned before coding starts, and test cases are
developed while the application is being designed and coded.
Debugging the code and correcting errors
Programmers tend to write the complete code and then begin debugging and
checking for errors. Though this approach can save time in smaller
projects, bigger and complex ones tend to have too many variables and
functions that need attention. Therefore, it is good to debug every
module once you are done and not the entire program. This saves time in
the long run so that one does not end up wasting a lot of time on
figuring out what is wrong. Unit tests for individual modules,
and/or functional tests for web services and web applications, can help
with this.
Guidelines in brief
A general overview of all of the above:
- Know what the code block must perform
- Indicate a brief description of what a variable is for (reference to commenting)
- Correct errors as they occur.
- Keep your code simple
- Maintain naming conventions which are uniform throughout.
Deployment
Deployment is the final stage of releasing an application for users.
No comments:
Post a Comment