A (Distributed) Developers’ Manifesto

It’s the post-git era of software development. We have lovely tools like artifact repositories, continuous integration, and code review robots that should alter how individual units of software are developed and delivered.

This is a short list of things I feel any project that has a lifespan longer than a few months should have. This helps on board new developers and helps mitigate integration testing complexity indirectly by addressing novel flow and failure modes with unit tests.

I preface everything with should because sometimes reality intervenes. For example if I know how a service should behave from a service contract then I can produce mocks and stubs to test for its failure modes and write tested branches of flow logic to handle that. If I don’t know what things are supposed to do then I have to fall back to record-and-playback or simulators to approximate integration behaviors in unit tests.

I state generic terms with specific (Java) terms to help disambiguate what I mean in a (Java) context. I personally work in numerous languages and each language will have its own specific case.

  1. Build Simply: Build any package (Jar) with only the build tool and the standard language toolchain itself (JDK).
    For example, I should not have to have special hardware, software, or other custom tools to build your low level library. I should not have to possess special system administration skills. I should not have to know about special modes of operations and their combinations ahead of time.
  2. Program Linguistically: Unit test any code (Class) on any platform that the language kit (JDK) runs on.
    For example, your special JNDI driver class will have branch logic that will fire based on hardware state. I should not have to possess that hardware to test your branch logic. I should not have to place that hardware unit into special states to test failure logic.
  3. Test Logically: I should be able to unit test any unit (Class) with 100% branch coverage without any special services (Database, Container Manager, Hypervisor, Network topology).
    For example, I should not have to install and initialize a special database for your one library. I should not have to initialize the database with special data. I should not have to load different data to test different scenarios.
  4. Fail Helpfully: Any module (package, jar, war, service, utility) should report its individual failure from an easy to trace root (single root exception class) for that module. 
    For example, the failure of the image module should trace to an image module exception with a cause of “file system full”. This exception only comes from the image module and it in turn gives the root cause of the failure. This should only be caught at the display layer or a layer that owns a module boundary. For example your image service should only throw image service exceptions.
  5. Operate Contractually: All modules (jar, war, utility, Class) should report what it does (Interface) but hide how it does it (Implementation).
    For example, I should be able to see a simple interface with explicit exceptions that are only the exceptions for classes that implement that interface. I should not have any implementation code mingle between packages. I should not have to be aware of implementation changes inside a 3rd party library.
  6. Layer only Cohesively: All flow logic (if-then-else, try-catch-finally) should be grouped together logically by abstraction; tracking only a single category of thing (security fault, file system fault, retry and timeout) at each layer.
    For example, code dealing with recovery from the failure of the happy path should be grouped together. If recovery isn’t possible then only the top most method (at the relative display layer of the module) should wrap the exception in the module’s root cause exception. This means your public interfaces are your relative display layer and will end up using package protected classes to orchestrate those behaviors internally. Do this in a way that follows the rules of cohesiveness.

These are hardly revolutionary points.