Jump to content

Software Design/Code organization conventions

From Wikiversity

This page lists possible code organization (complexity, arrangement, structure) conventions that may be established in a codebase.

The goal of this page is to list all conceivable code organization conventions and rules regardless of their perceived importance and whether or not it's practical to enforce them in projects. See Conventions category for further information.

Possible conventions

[edit | edit source]

Prohibited language constructs

[edit | edit source]

Whether there are constructs and features of the programming language that must not be used in the codebase. Examples of such constructs and features that may be considered inherently error-prone and/or difficult for understanding:

Limits on code complexity and size metrics

[edit | edit source]
  • Number of elementary components in boolean expressions. For example, an expression like (a && b) || (c && d) || e (5 elementary components) could be considered too complex and require to extract some part of it into an explaining variable. Implementations of functions like equals() may be exempted from this convention: they may include boolean expressions with many components like fieldA.equals(other.fieldA) && fieldB.equals(other.fieldB) ... && fieldZ.equals(other.fieldZ).
  • Level of parentheses nesting in boolean expressions
  • Level of nesting of ternary expressions (if nested ternary expressions are not prohibited at all: see Prohibited language constructs section above)
  • Number of parameters to a function[5]
  • Number of lines of code in a function
  • Cyclomatic complexity of a function. Developers are not expected to estimate the cyclomatic complexity of functions manually, so unlike most other conventions this one can't be a verbal convention which developers follow casually. This convention must be enforced using automatic tools:
  • Level of block nesting (decision nesting)
  • Function fan-out: how many different functions are called from a function (including itself, if the function is recursive)[6]
  • Number of different classes referenced from a function. In Java, this convention may be enforced with "Overly coupled method" inspection in IntelliJ IDEA.
  • Number of different classes referenced from a class. In Java, this convention may be enforced with "Overly coupled class" inspection in IntelliJ IDEA.
  • Number of fields in a class
  • Number of functions in a class
  • Number of lines of code in a class
  • Number of classes in a package

Arrangement of functions and fields

[edit | edit source]
  • Whether static members (fields, functions) must be all arranged before (or after) all non-static members in a class or static and non-static members could be interleaved.
  • Whether fields must all be arranged before all functions in a class or fields and functions could be interleaved.
  • Whether public members must be arranged before non-public members in a class or public and non-public members could be interleaved.
  • Whether functions that don't modify the state of a class (getters) must be arranged before (or after) functions that modify the state of a class (setters) or getters and setters could be interleaved.
  • Whether functions in a class that belong to some extended abstract class must be arranged in the same order as they are declared in the abstract class.
  • What is the principle of function arrangement in a class (after all principles mentioned above which may be applied earlier). It may be:
    • According to the order in which the functions are called: for example, function start() is defined before finish()
    • Importance for understanding the class
    • "Interestingness" of the functions
    • Development risk: "trickiness" of functions, the probability of them containing bugs
    • Runtime risk: the probability that something "bad" happens in the functions
    • "Hotness": how often the functions are expected to be called, how important are they for the performance of the class
    • Alphabetical order
  • Whether functions are arranged in depth-first or breadth-first calling order. Examples:

Depth-first calling order:

fun f1() { f2() }
fun f2() { f3() }
fun f3() {}
 
fun f4() { f5() }
fun f5() {}

Breadth-first calling order:

fun f1() { f2() }
fun f4() { f5() }
 
fun f2() { f3() }
fun f5() {}
 
fun f3() {}
  • Whether a (private) function that is called from multiple other functions should be arranged before all its callers, or after all its callers, or in a "subtree" of callees of the first (or the last) caller if depth-first calling order is used to arrange functions.

Branch arrangement in if-else and switch statements

[edit | edit source]

The criterion for arranging a branch higher in an if-else statement or a chain may be:

  • Non-negativity in the if condition
  • Importance for understanding the function
  • "Interestingness" of the branch
  • Size of the branch
  • "Hotness": how often the branch is expected to be taken, how important is it for the performance of the function. In languages such as C and C++, the probability of the branches may be hinted to the compiler via macros such as likely() and unlikely()[7] independently of the order of the branches, yet it still may be reasonable to use this convention to communicate the expected runtime behavior in the structure of the code.

In addition to the possible principles of branch arrangement in if-else statements listed above, the principle of case arrangement in switch statements may also be

  • The alphabetical order of case expressions or variables
  • The order of declaration of the enum values if they are used as the case constants.

See also

[edit | edit source]

References

[edit | edit source]
  1. Boswell, Dustin; Foucher, Trevor (2011). The Art of Readable Code. ISBN 978-0596802295.  Chapter 7, "The ?: Conditional Expression (a.k.a. "Ternary Operator")" section
  2. Boswell, Dustin; Foucher, Trevor (2011). The Art of Readable Code. ISBN 978-0596802295.  Chapter 7, "Avoid do/while Loops" section
  3. Boswell, Dustin; Foucher, Trevor (2011). The Art of Readable Code. ISBN 978-0596802295.  Chapter 7, "The Infamous goto" section
  4. Why aren't user-defined operators more common? question on StackOverflow
  5. Are there guidelines on how many parameters a function should accept? question on StackOverflow
  6. Page-Jones, Meilir (1988). The Practical Guide to Structured Systems Design (2 ed.). ISBN 978-8120314825. 
  7. How do the likely/unlikely macros in the Linux kernel work and what is their benefit? question on StackOverflow