Codewell Programming Standards

Version 1.3


1. Introduction

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.

2. Definitions

Comment
Free form text which adds information that cannot easily be understood from reading the compilable code.
Comment, Block
A multi-line comment which may be enclosed within a single pair of comment delimiters.
Comment, Endline
A comment on the right end of a source code line.
Comment, Paragraph
A comment, which may be multi-line, above a section of code; the purpose of the comment is to explain that code. It may explain the method or algorithm used or may just briefly describe the function(s) performed. The comment text shall be at a higher level of abstraction than the code.
Function (C/C++ Language)
A function is a C function that returns a value. In general, the principal purpose of a function is to retrieve that value, and it is used syntactically in the same way as a variable. See Procedure below.
Horizontal Rule
A very obvious horizontal delimiter, synthesized of mostly hyphen characters ("-") spanning a specific number of columns.
Module
A source file.
Module, Header
A source file that contains only definitions; i.e., it does not contain any executable statements. Its file name includes an extension of ".h".
Module, Code
A source file that contains executable code. Its file name includes an extension of ".c".
Module, Resource
A source file that contains resource definitions. Its file name includes an extension of ".rc".
Module Identifier
The Block Comment at the beginning of a Module.
Procedure (C/C++ Language)
A procedure is a C function that has either a void return or just returns a status. In general, the principal purpose of a procedure is to perform an action, and it is used syntactically as a "command". See Function above.
Routine
A function or procedure.
Routine Identifier
The Block Comment at the beginning of a Routine.

3. Specifications and Documentation

3.1. General.

All documents shall be ASCII Text Files (.txt extension).

All documents shall be maintained in the Source Code Control System.

3.2. Format.

All specifications shall follow the Codewell standard format, which includes the following elements.

3.2.1. Alternate Format (HTML).

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.

3.3. Functional Specification.

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.

3.4. Implementation 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.

4. Software System Organization

4.1. Subsystem

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.

4.2. Module

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.

4.3. Routine

A Routine is a procedure or function that performs a small number of functions (preferably a single function).

5. Names, Constants and Literals

5.1. Meaningfulness of Names

A name shall be meaningful; i.e., it shall unambiguously describe the element to which it is attached.

5.2. Formats of Names

The format of a name shall indicate one or more of its properties.

5.2.1. General

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.

5.2.2. Variable, Type and Constant Names

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:
prefix indicates
k a constant
Scope, etc.:
prefix indicates
c a class variable
g of global scope
i an instance variable
m of module scope
Quantity, etc.:
prefix indicates
a an array
p a pointer
Interpretation:
prefix indicates
u is unsigned
Type:
prefix indicates
b a boolean
c a character
d a double-precision floating-point number
h a handle
i an integer
l a long
n a short
s a single-precision floating-point number
v is void
z a string (in C, must be a #define)
other a user-defined 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

5.2.3. Procedure and Function Names

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.

5.2.4. Module and File Names

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.).

5.3. Literals

5.3.1. Numeric Literals

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.

5.3.2. Text Literals

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.

6. Source Code

6.1. Source Code Control

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.

6.2. Readability

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.

6.2.1. White Space

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.

6.2.2. Source Characters

All source files must be printable by standard text file print software.

6.2.3. Source Lines

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 )
    );

6.2.4. Pointer and Reference Notation

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

6.2.5. Columns

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

6.2.6. Source Editor

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.

6.2.7. Locality

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.

6.2.8. Embedded Assignment Statements

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.

6.2.9. Important Exception

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.

6.3. Module

6.3.1. Organization

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.

6.3.2. Identification

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.

6.4. Header Module

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.

6.4.1. System Header Module

There shall be a System Header Module for each System, containing definitions global within the System.

6.4.2. Subsystem Header Module

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.

6.4.3. Library Header 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.

6.5. Code Module

6.5.1. Content

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.

6.5.2. Scope of Names

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.

6.5.3. Sequence of Code within the Module

The sequence of Module elements shall be as follows.

  1. a blank line or a #pragma once line
  2. identification block comment
  3. #include statements
  4. constant definitions
  5. macro definitions
  6. type definitions
  7. module-scope variables and routine
  8. global-scope routine(s)

The 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.

6.6. Routine

6.6.1. Content

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.

6.6.2. Identification

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.

6.6.3. Size

It is preferable that Routines not be larger than about 200 lines of source code, including all comments and blank lines.

6.6.4. Scope of Names

The scope of all names shall be limited to the current Routine, except where it is necessary to provide visibility to other Routines.

6.6.5. Declaration

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]
    );

6.6.6. Definition

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]
    }

6.7. Executable Code

6.7.1. Language

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.

6.7.2. Variable Usage

Each variable shall be used for a single purpose.

6.7.3. Indentation and Braces

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.

6.7.3.1. Routine code

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.

6.7.3.2. if Statements

Every 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)]
    }

6.7.3.3. Compound if Conditions

Each 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.

6.7.3.4. for Statements

The 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)]
    }

6.7.3.5. switch Statements

Every 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;
    }

6.7.4. Loops

break and continue statements are allowed within loops, but they must be clearly commented.

6.7.5. Returns

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 );
    }

6.7.6. Parentheses

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.

6.7.7. Macros

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.

6.7.8. Debugging Code

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.

6.8. Resource Module

6.8.1. Content

A Resource Module shall contain Resource Definitions for a single Source Module.

6.8.2. File Name

A Resource Module shall have the same name as the associated Source Module, except for the extension.

6.8.3. Resource Names

Resource Names shall follow a standard to be defined.

6.8.4. Readability

Resource Files shall follow, insofar as it is possible, the Readability guidelines specified in this document.

7. Auxiliary Files

The Batch Files, Make Files and other command files which control development software shall be the standard Codewell files.

7.1. General

7.1.1. Readability

Auxiliary Files shall follow, insofar as it is possible, the Readability guidelines specified in this document.

7.2. Batch Files

7.2.1. Naming

A Batch File shall have a mnemonic name indicating its function. It shall have the extension ".bat".

7.2.2. Occurrence

There shall be a single Batch File for each function to be performed.

7.2.3. Help

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.

7.3. Make Files

7.3.1. Naming

A Make File shall be named "Makefile" or "GNUmakefile" (these are common default names).

7.3.2. Occurrence

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.

7.3.3. Clean Section

There shall be a target in every Make File, named clean, that deletes all files created by normal invocation of the Make File.


Go to Top of page
Go to Codewell Corporation page
Codewell Programming Standards /
15 Allen St., No. 2
Arlington, MA 02474-6809
(781) 641-3327
Last Updated: 12 Apr 2007
Valid HTML 4.01!
[ validate ]

Copyright (C) Codewell Corporation, 1995-2007. All Rights Reserved.