Cognitive bias and the difficulty of evolving strongly typed solutions

Functional Exchange 2013 featured an interesting talk by Paul Dale that had been mostly gutted but had a helpful introduction to cognitive bias. That reminded me of something I was trying to articulate about in my own talk when I was talking about the difficulty of evolving typed solutions. I used the analogy of the big ball of mud where small incremental changes to the model of the system result in an increasing warped codebase. Ultimately you need someone to come along and rationalise the changes or you end up with something that is ultimately too difficult to work with and is re-built.

This of course is an example of anchoring where the initial type design for the system tends to remain no matter how far the domain and problem have moved from the initial circumstances of their creation.

Redesigning the type definition of a program is also expensive in terms of having to create a new mental model of what is happening rather than just adapting the existing one. Generally it is easier to adapt and incorporate an exception or set of exceptions to the existing model rather than recreating an entire system.

Dynamic or data-driven systems are more sympathetic to the adaptive approach. However they too have their limits, special cases need to be abstracted periodically and holistic data objects can bloat out of control into documents that are dragging the whole world along with them.

Type-based solutions on the other hand need to be periodically re-engineered and the difficulty is that the whole set of type definitions need to be worked on at the same time. Refactoring patterns of object-orientated code often focus on reorganisation that is easy, such as pulling out traits or extracting new structures. This is still anchoring on the original solution though.

If a type system is to be help rather than a hindrance you need to be rework the overall structure. I think with most type-systems this is actually impossible. Hence the pattern of recreating the application if you want to take a different approach.

The best typed solution currently seems to be type unions where you can make substantial changes and abstractions but not have to work about things like type hierarchies or accidentally polluting the edge cases of the system.

Where these aren’t available then good strongly-typed solutions actually rely heavily on good, proactive technical leaders to regularly drive good change through the system and manage the consequences.


Why Repositories and not Services?

This is a good question. Why do people like ThoughtWorks make a lot of fuss about things like Services but then want to use things like the Repository pattern when writing code?

The short answer is that Service Orientation and Domain Driven Design have two slightly different concerns.

For example, in a transportation domain you don’t get a Truck from a Truck Service you get a Truck from the Garage or the Truck Manufacturer depending on whether you own it or are buying it. The point being that a Truck Service in Domain Design terms is meaningless, it is just something that programmers introduce to make their code easier for them to use.

If however you want to track where a Consignment is then it makes sense to offer this as a service. For a start it has different audiences; I might want to offer tracking to customers via the web and a slightly more detailed version of the service to the Customer Service Department.

In this sense the Tracking Service is actually a Domain item, people actually talk about the Tracking Service and the Service has been organised around the transactions and expectations that happen in the course of transporting goods.

I am not sure if it makes any sense to talk about a Service in your codebase if it does not have an external consumer for its functionality. Usually Service objects that only interact with your own code can be broken up and have their concerns divided in a different way so as to eliminate them. A Truck Finder for example might make sense, it would handle finding out whether a Truck was on the Road, in the Depot or in the Repair Shop. The Depot might then tell you whether the Truck was being loaded while the Truck could tell you how full it currently was.

Once you have identified external consumers for a service then you get into the question of Service Contracts and a lot of the good things about Service Architectures begin to apply. Limiting concerns, platform neutrality and service composition for example; but this involves a lot more than just tacking the word “Service” on the end of a class.