Vaclav Kosar's face photo
Vaclav Kosar
Software And Machine Learning Blog

How to Structure Code: Localize Related, Inline over Extract, Specific over Generic

A short alternative view on code structure principles based on several years of personal experience, Carmack, Jonathan Blow, and Adam Bien's posts.

However, as with all the rules, do not put the horse before the caret. Don’t optimize code structure too early, and don’t be too stringent. Focus more on the specifics of your problem than on generics offered by the ivory tower books.

Sections:

Code structure cuts across the architecture. Learn about dead-simple architecture called Boundary-Control-Entity here.

Code Containers

Code Containers is a term used in this post to refer to a instance of a level of hierarchy into which code can be sorted.

In Java, code is sorted into following hierarchy:

  • function or field
  • class
  • package
  • maven module
  • micro-service

The code structure is improved by sorting Code Containers which are related into single higher hierarchy Code Container.

Related Code Containers are defined by following:

  • Related Code Containers often use same Code Containers.
  • Programmers are able to learn to often anticipate membership of unfamiliar Code Container in Related Code Containers.
  • Deployment lifecycle, stability and monitoring are often the same for Related Code Containers.
  • Same programmers often work on the same Related Code Containers.

Examples

Functions a and b both call function c, thus they are related. Because they are related they are placed into higher container A.

 class A {
   
   int a(int v) {
     return c(v)*2;
   }
   
   int b(int v) {
     return c(v)*3;
   }
   
   private int c(int v) {
     return v + 1;
   }
 }

Both classes import same class B. In absence of other restrictions they can be placed within same package as B.

import B;
class A { }
import B;
class C { }

Related packages which are used by two different modules are can be placed into one common Maven module. This module can be then used as Maven dependency.

Micro-service as a Maven module

Micro-services in Java are defined by a Maven module. Final micro-service executable jar then usually contains the defining module jar and all its dependencies.

References

  • Real world objects often fulfill the criteria. Particularly the second one. Real world objects are recommended as first candidates for object creation (“Find Real-World Objects”; Code Complete)
  • “Classes that contain strongly related functionality are described as having strong cohesion, and the heuristic goal is to make cohesion as strong as possible.”; Aim for Strong Cohesion; Code Complete
  • “Coupling describes how tightly a class or routine is related to other classes or routines.”; Keep Coupling Loose, Code Complete.
  • “Try to create modules that depend little on other modules.”; Keep Coupling Loose, Code Complete.
  • “in reality, they are little more than convenient carrying cases for loosely related collections of data and routines.”; Class Foundations: Abstract Data Types (ADTs), Code Complete
  • Generic Class Name Signals Low Cohesion

2. Inline over Extract

I used to have a section referencing Martin Fowler and divide and conquer strategy about keeping methods short and low complexity. But since then I came around to view of the productive programmers like Carmack and Jonathan Blow. After testing the idea myself, I do think that longer methods are more productive than lots of small methods. Extracting small methods makes the code less transparent, harder to refactor, and harder to write. A complexity is better exposed attempted to be hidden.

The function that is least likely to cause a problem is one that doesn't exist, which is the benefit of inlining it.

Example

USD rate below is extracted into a separate Code Container - into a field. Replacing magic value with named field increases human readability. While method transformToUsd is simplified, the class itself becomes more complex. This exemplifies prioritizing local complexity over total complexity.

class CurrencyAmountTransformer {
    
    private double USD_RATE = 1.2;
    
    double transformToUsd(double amount) {
        return USD_RATE * amount;
    }
    
}

3. Specific over Generic

Do not generalize too early. Do waste time trying to write general solution to broad classes of problems, which you’ll never face. Try to finish on time and make it work robustly for the specific case you are solving.

Continue: The Dead-Simple Architecture

Code structure cuts across the architecture. Learn about a dead-simple architecture called Boundary-Control-Entity here.

04 Jun 2017