Modern quality management techniques require that quality be "designed in" to a product, rather than being "tested for" after the product has been built. In order to determine that quality work is being performed, it is necessary to have a set of standards to which that work can be compared.
This document establishes a set of Programming Standards that apply to all software produced by Codewell developers.
The intention is that code produced under these standards is easy to read and understand. As a result, it is easy to test, debug and maintain. It may not be the most efficient code, but that is rarely important, as the cost of hardware speed and memory continue to drop. It should always be assumed that someone other than the original author will be making changes in the future.
This document assumes the source language is ANSI C/C++. There are similar documents for other languages used at Codewell. When a language for which no standard exists is used, the programmers should attempt to use constructs as similar as possible to those discussed herein.
-") spanning a specific number of
columns.All documents shall be ASCII Text Files (.txt extension).
All documents shall be maintained in the Source Code Control System.
All specifications shall follow the Codewell standard format, which includes the following elements.
When there is compelling reason, a specification may be written in human-readable HTML. Markup should minimally detract from readability. As an example, this document is written in human-readable HTML.
The Functional Specification is the definition of the job, stated in the client's terms. It shall include as much as is known about files, file contents, network traffic, etc. It shall be reviewed with the Initiating group.
The Functional Specification shall contain everything necessary to be the source material for the user's manual and the acceptance test.
The Functional Specification shall be released with the corresponding software.
When the software is part of a defined product, the Functional Specification shall be part of the Product Specification.
The Implementation Specification is the first draft of the final software documentation. It shall be produced and maintained in machine-readable form.
The Implementation Specification shall begin with an overview of the program(s) to be developed. This overview shall employ whatever form of process description will enable a skilled programmer to most easily understand the program(s); e.g., flow charts, state diagrams, data flow diagrams, etc.
The Implementation Specification shall contain the breakdown of the system into subsystems and modules, with the rationale for so doing. It shall describe the interfaces between the various subsystems and modules.
The Implementation Specification, itself, shall be broken down on the same basis as the software, with a section devoted to each subsystem and module.
The Implementation Specification shall contain definitions of all data structures in the program(s); e.g., internal structures, records, files, network packets, etc.).
The Implementation Specification shall contain definitions of all screen images to be displayed to the user.
The Implementation Specification shall contain definitions of all error messages.
The Implementation Specification shall contain everything necessary to be the source material for the test plan writers and the programmers.
The Implementation Specification shall be kept updated during software implementation and shall be released with the corresponding software.
A System shall be broken down into Subsystems according to a stated rationale. A goal shall be to minimize the interactions between Subsystems. The interfaces between Subsystems shall be completely defined in the Implementation Specification.
A Subsystem may be broken down into lower-level Subsystems according to a stated rational.
A lowest-level Subsystem shall comprise one or more Modules.
A Module shall comprise the source code which is a compilable unit.
A Subsystem shall be broken down into Modules according to a stated rationale. A goal shall be to minimize the interactions between Modules. The interfaces between Modules shall be completely defined in the Implementation Specification.
A Module shall comprise one or more Routines.
A Routine is a procedure or function that performs a small number of functions (preferably a single function).
A name shall be meaningful; i.e., it shall unambiguously describe the element to which it is attached.
The format of a name shall indicate one or more of its properties.
A name shall consist of full English words, concatenated into a single word, with the first letter of each English word in upper case, and all others in lower case. Even acronyms which are normally written as all upper case shall follow this format. Digits are allowed where meaningful. Abbreviations are not allowed.
For example, SmtpHostAddress is acceptable;
SMTPhostAddr, host_address,
HostAddr, and smtp_host_address are not.
Each such name follows the general rule, but is preceded by a "Hungarian Notation" prefix, indicating its use.
User-defined Type (typedef and enum, and
named structs and classes in C++) Names begin
with a 't'. Enumeration value names begin with an
'e_'.
Variable and Constant Names begin with combinations of the following:
| Constancy: | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| Scope, etc.: | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| Quantity, etc.: | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| Interpretation: | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| Type: | ||||||||||||||||||||||||
|
When multiple prefix letters are necessary, they are combined in the following order:
[[k][c|g|i|m]_][a|p][u][b|c|d|h|i|l|n|s|v|z|other].
Thus, a global constant array of booleans might be
kg_abSample, and an instance integer might be
i_iAnInteger. Note that an underscore ('_')
separates c, g, i,
m, and k from the remaining characters.
As mentioned above, the name of a Variable of a user-defined Type
begins with a user-defined prefix. This prefix should be specified along
with the definition of the type. The name of the type begins with
't'. For example:
typedef enum // how to behave..
{
e_mbServer, // as a server
e_mbDaemon, // as a daemon
e_mbCOUNT // count of behavior kinds
}
tMyBehavior; // mb
tMyBehavior m_mbWhatToDo = e_mbDaemon; // how this program behaves
Another example:
typedef struct _tDataSet // a set of data..
{
int iSimple; // simple number
char acText[10]; // a few characters
struct _tDataSet * pdsNextSet; // pointer to next set
}
tDataSet; // ds
tDataSet m_dsMyStuff = { 0, "", NULL }; // my data set
A Procedure or Function Name shall comprise words describing the
routine's functionality. In general, it shall start with the name of the
object being processed, end with the verb that describes the processing,
with any modifiers in between. For example, a routine to fetch the next
Username might be named UsernameNextFetch.
The name of the File containing a Module shall be the same as the Module Name, as limited by the rules for File Name construction. The name of each Module shall be a close approximation of the name of the principal function or data contained therein.
File Names shall be compatible with as many filesystems as practical
(specifically MS Windows and Unix), shall not contain any spaces, and
shall include the standard appendices (e.g.: '.c',
'.cpp', '.rc', '.h', etc.).
All Numeric Literals except 0 and 1 shall
be defined as Constant Names. The literal 0 is allowed in
tests. The literal 1 is allowed as an incrementer.
To ease the translation into languages other than English, it is preferable that Text Literals are stored in message files, rather than written into the code.
All Source Code shall be maintained in the Source Code Control System.
When the Development team produces a delivery of any kind, nothing is delivered that has not come from the Source Code Control System, or been generated therefrom.
Source code is compiled by software, but is read by humans. It is important that it be as readable as possible. There are some general rules that are known to improve readability.
Blank lines shall be used liberally. In particular, there shall be at least one blank line before each Paragraph Comment.
There shall be at least one space character after the start comment
delimiter ("//" or "/*") before the start of
text.
There shall be at least one space character between each syntactic element on a line, except for certain punctuation elements. There shall be no trailing whitespace at the end of a line. There shall be no trailing blank lines at the end of a file.
Each left or right curly brace ("{" and
"}") shall be on a line by itself.
All source files must be printable by standard text file print software.
There shall never be more than one statement on a source line. This rule specifically forbids more than one delimiting semicolon on a source line, plus any form of embedded assignment statement.
When a command is too long to fit on one source line, it shall be broken into multiple lines in a rational manner. When the command to be broken is a multi-argument function call, it shall be broken by placing each argument on a separate line, aligned to the first argument; for instance:
iValue =
ObjectIntegerGet
(
iArgument1,
...
iArgumentN
);
When a string is too long to fit on one source line, it shall be broken into multiple lines by writing it as separate strings (which the compiler concatenates); for instance:
strncpy
(
acTarget,
"Now is the time for all good men"
" to discover that the quick brown"
" fox jumps over the lazy dog.",
sizeof ( acTarget )
);
The pointer '*' and reference '&'
indicators are part of the type, not of the name. Therefore, each shall
be preceded by a space and followed by a tab or newline.
char * pcFoo; // good declaration
char* pcFoo; // bad declaration
char * pcFoo; // bad declaration
char *pcFoo; // bad declaration
The Tab character shall be equivalent to 4 space characters. Whenever possible, lists should be organized in columns. For example, groups of definitions should be aligned as follows:
unsigned int uiName; // some comment
int iName; // some comment
float sName; // some comment
double dName; // some comment
char cName; // some comment
Each programmer may use the Text Editor with which he or she is most comfortable, as long as it includes the necessary controls; e.g., the ability to have 4-space Tabs. It is preferred that the Tabs not be expanded into spaces in the file.
It is important that all definitions and declarations occur as close as possible to the source lines in which they are referenced. Thus, items used in a single Routine shall be defined or declared in that Routine; items used in a single Module shall be defined or declared at the top of that Module; etc.
Embedded Assignment Statements are not allowed. That is, the form
lval = rval is not allowed as an argument to
a function, an if statement, or any other statement.
When modifying code that was not written to these specification, each modified function or procedure shall be updated to meet this standard, but unaffected parts of the code must be left as is.
A Module shall be contained in a single source file, and each source
file shall contain a single Module. Use of #include to
combine .c files is not allowed.
The first lines in every Module shall be a Module Identifier that includes the following:
The format of the Module Identifier shall be as follows:
#pragma once
//------------------------------------------------------- ... ----
//
// [a one-line identification]
//
// Copyright (C) Codewell Corporation, 200?-200?.
// All rights reserved.
//
// Codewell Corporation confidential.
//
// References:
//
// [name] -- [description]
// ...
// [name] -- [description]
//
// Description:
//
// [multi-line description]
//
// Notes:
//
// [multi-line notes]
//
// Revision History:
//
// YYYY/MM/DD [name]
//
// [multi-line comment of change]
//
//------------------------------------------------------- ... ----
[blank line]
The "#pragma once" line is used in .h
files. In all other files, this should be a blank line.
The Description shall include the purpose and function of the Module.
There shall be one entry added to the Revision History each time the module is checked in to the Source Control System. The entries in the Revision History shall be in order by date (most recent entry first). There shall be one comment for each change made. Each comment shall include: the name of the object (definition, function, procedure, etc.) changed, the nature of the change, and the reason for the change.
If either of the "References:" or "Notes:" sections are empty or inappropriate, it may be elided.
The horiaontal rules are 95 columns wide.
A Header Module contains nothing but definitions; i.e., it contains no executable code.
Each definition shall be immediately preceded by an explanatory Comment. Groups of related definitions may be preceded by a single Comment.
Each definition shall be defined once and only once within a system.
There shall be a System Header Module for each System, containing definitions global within the System.
There shall be at least one Subsystem Header Module for each Subsystem, containing definitions local to that Subsystem and global to one or more of its Modules. It is acceptable to have more than one, providing their purpose is clearly defined and described.
While a definition that affects only a single Code Module usually
belongs in that Module, it may aid understanding to place a whole group
of related definitions together in the Header Module. For example, all
message definitions belong in a single messages.h file,
even though many are referenced in only one module.
There shall be a Library Header Module for each Library. This Module declares the entire external interface to the Library, or it includes all the Header Modules required to achieve the same effect.
A Code Module shall contain executable code in one or more Routines. The Description section of the Module Identification shall include a stated rationale for the specific Routines included in a Module.
The scope of all names shall be limited to the current Module, except where it is necessary to provide visibility to other Modules.
There shall be a minimum of global variables. When a global variable is necessary, the reason for making it global shall be stated.
The sequence of Module elements shall be as follows.
#pragma once lineThe definitions shall be of the same form as those in Header Modules. Groups of definitions shall be separated by blank lines.
The use of macro definitions shall be kept to a minimum. When used, their definitions shall always be surrounded with parentheses.
No standard language element shall be redefined in any way.
A Routine shall perform a limited number of related operations (preferably one). There shall be a stated rationale for the specific operations performed by a Routine.
The first lines of every Routine shall be a Routine Identifier that includes the following:
The format of the Routine Identifier shall be as follows:
[blank line]
[blank line]
//------------------------------------------------------- ... ----
//
// [multi-line description]
//
// On Entry:
//
// [conditions necessary]
//
// On Return:
//
// [conditions resulting]
//
// Limits:
//
// [limits on arguments]
//
// Notes:
//
// [multi-line notes]
//
//------------------------------------------------------- ... ----
[blank line]
The "multi-line description" should describe some detail about how the routine does its work.
The "On Entry" section does not describe arguments. Arguments are described in the routine declaration.
The "On Return" section does not describe return values. Return values are described in the routine declaration.
The horiaontal rules are 95 columns wide.
It is preferable that Routines not be larger than about 200 lines of source code, including all comments and blank lines.
The scope of all names shall be limited to the current Routine, except where it is necessary to provide visibility to other Routines.
Every Routine shall be declared by a Function Prototype, either at the top of the Code Module or in a Header Module.
The routine shall be declared in the ANSI C/C++ format, as follows:
[blank line]
[type] // [comment]
[routine name]
(
[type] [argument name], // [comment]
...
[type] [argument name] // [comment]
);
The routine shall be defined in the ANSI C/C++ format, as follows:
[blank line]
[type] // [comment]
[routine name]
(
[type] [argument name], // [comment]
...
[type] [argument name] // [comment]
)
{
[type] [variable name]; // [comment]
...
[type] [variable name]; // [comment]
[blank line]
[executable code]
[return statement]
}
All code shall conform to ANSI C/C++ Standards.
The use of any Preprocessor statements is discouraged, with the exception of #include and #define. The use of #define shall be limited to simple substitutions for the purpose of making the code more readable or more flexible.
Each variable shall be used for a single purpose.
Indentation shall be 4 spaces. There shall be no more than ten levels of indentation.
The left and right braces of a pair shall be aligned in the same column. That column shall be the column of the command to which the braces belong.
A new level of Indentation shall begin each time a left brace occurs. The code included between braces shall start on the line below the left brace, one indentation to the right.
Routine declarations shall begin at the left margin. A Routine's declarations and executable code shall begin one indentation to the right of the margin.
All of the routine code lines shall begin one indentation from the left margin; for example:
[blank line]
[type] // [comment]
[routine name]
(
[type] [argument name], // [comment]
...
[type] [argument name] // [comment]
)
{
[type] [variable name]; // [comment]
...
[type] [variable name]; // [comment]
[blank line]
[executable code]
[return statement]
}
There shall be a blank line between the last variable declaration and the first executable statement.
if StatementsEvery if statement shall contain a set of braces, even
if there is only one result statement; for example:
if ( [condition] )
{
[statement(s)]
}
if ( [condition] )
{
[statement(s)]
}
else
{
[statement(s)]
}
if ( [condition 1] )
{
[statement(s)]
}
else if ( [condition 2] )
{
[statement(s)]
}
else
{
[statement(s)]
}
if ConditionsEach conditional expression of a compound if statement
shall occupy a separate line; for example:
If
(
( [condition 1] )
&&
(
( [condition 2] )
||
( [condition 3] )
)
)
{
[statement(s)]
}
It is always permissible to use even more white space; for example,
the parentheses around each [condition n] above could be on
separate lines.
for StatementsThe preferred format of the for statement is similar to the following:
for ( iIndex = 0; iIndex < k_iLimit; iIndex++ )
{
[statement(s)]
}
The following format is also acceptable; this allows a comment on each component of the condition set. This form should always be used when the condition set components are complex.
for
(
iIndex = 0; // [comment]
iIndex < k_iLimit; // [comment]
iIndex++ // [comment]
)
{
[statement(s)]
}
switch StatementsEvery switch statement shall include a
default case. Every case within a
switch statement (including the default case)
shall terminate with a break statement, except where
multiple cases share the same code. If there is any code between two
case statements which "fall through", a comment shall so
indicate. All of the valid cases are illustrated below.
switch ( [expression] )
{
case 1:
[statement(s)]
break;
case 2:
[statement(s)]
break;
case 3:
case 4:
case 5:
[statement(s)]
break;
case 6:
[statement(s)]
// fall through
case 7:
[statement(s)]
break;
default:
[statement(s)]
break;
}
break and continue statements are allowed
within loops, but they must be clearly commented.
The last statement of a Routine shall be a return
statement.
It us usually preferable that the last statement be the only
return statement in a Routine, but the use of
returns in statements at the top of a routine which check
arguments is encouraged. For example:
int // [comment]
LimitCheck
(
int iItem, // [comment]
double dValue // [comment]
)
{
// validate arguments
if ( iItem < k_iLowerLimit )
{
return ( FALSE );
}
// do work
[statement(s)]
return ( TRUE );
}
As illustrated in the examples above, there shall always be white space between any parenthesis and an adjoining word or number. This white space is not necessary between two punctuation characters.
The use of Macros is discouraged. If it can be written as a function, it should be.
Macros must be defined at Global or Module level, never inside a block.
All debugging code shall be plainly marked and delimited by
#ifdef statements. In general this code should be left in
the released code for the convenience of the maintenance programmer.
A Resource Module shall contain Resource Definitions for a single Source Module.
A Resource Module shall have the same name as the associated Source Module, except for the extension.
Resource Names shall follow a standard to be defined.
Resource Files shall follow, insofar as it is possible, the Readability guidelines specified in this document.
The Batch Files, Make Files and other command files which control development software shall be the standard Codewell files.
Auxiliary Files shall follow, insofar as it is possible, the Readability guidelines specified in this document.
A Batch File shall have a mnemonic name indicating its function. It shall have the extension ".bat".
There shall be a single Batch File for each function to be performed.
Whenever a Batch File is invoked with no arguments or with "-?", "-h", or "-help", it shall display one screen (max 49 lines x 79 characters) of information.
A Make File shall be named "Makefile" or "GNUmakefile" (these are common default names).
There shall be a Make File in each directory that contains one or more Source Modules. That Make File shall build every output comprising those Source Modules.
There shall be a target in every Make File, named clean, that deletes all files created by normal invocation of the Make File.
Codewell Programming Standards
/
15 Allen St., No. 2
Arlington, MA 02474-6809 (781) 641-3327 Last Updated: 12 Apr 2007 |
[ validate ] |