Managing software dependencies: a lesson from the credit crunch

This article was originally published in VSJ, which is now part of Developer Fusion.
“What on Earth do the credit crunch and software design have in common?” I hear you ask.

The answer is simple: dependencies.

If Peter lends £100 to Paul, then that creates a credit dependency between Peter and Paul. Peter is depending on Paul to pay that money back, with interest. If Paul is unable to pay it back, and defaults on the loan, then that could hurt Peter.

In turn, if Mary lent Peter £200, out of which Peter lent £100 to Paul, then if Paul defaults on Peter’s loan, there’s now a risk that Peter cannot pay back the £200 to Mary.

These defaults can spread along credit dependencies, creating ripples that spread through the network.

Similarly, a change to a line of code in one method, class or package can spread to other methods, classes and packages that depend on them. In turn, those changes can propagate out to indirectly dependent modules, creating ripples through the code. This ripple effect can make small changes very expensive, and the size of these ripples is a strong determining factor in the cost of changing the software.

There are certain factors that tend to make ripples in dependency networks bigger and more costly:

  • The total number of dependencies
  • The packaging of dependent components
  • The direction in which the dependencies point
Imagine a town where everybody has borrowed money from everybody else. This is a very highly interconnected dependency network. All it would take is for one borrower to default, and the effect could spread throughout most of the network, plunging the entire town into poverty. It’s pretty much the same mechanism that produces massive forest fires – the more trees there are, and the closer together the trees are, the further the fire is likely to spread.

Similarly, if every class in your code depended on every other class, making a small change to one class could result in having to make changes to the whole code base.

In all three cases – credit networks, forest fires and software design – the first strategy we can employ to minimise the ripple effect is to reduce the number of dependencies.

In credit networks, we’re simply talking about making fewer loans. Full stop. If less people owe money to other people, then there are fewer avenues for defaults to spread down.

In forest fires, if there are fewer trees and the trees are further apart, it will be harder for fire to spread. Indeed, one of the more popular strategies for avoiding rare but massive forest fires that can destroy millions of acres is to allow small and more frequent fires that deforest hundreds of acres to burn themselves out. This way, forests tend not to become too populous and the spread of fire is easier to contain.

In software designs, if we write less code, then we will end up with fewer dependencies (since it’s incredibly difficult to write code without introducing dependencies). If we keep our designs as simple as we can make them to satisfy the requirements, and mercilessly refactor our code to remove duplication, we should end up with as little code as we can get away with writing.

But we cannot eliminate dependencies entirely. You cannot have credit without creating lenders and borrowers. A forest with few or no trees – placed very far apart – is technically a field. And just try writing code without introducing dependencies!

For the dependencies that we can’t avoid creating, we can still do more to limit the ripple effect.

Firstly, we can localise dependencies. One of the most alarming features of the current credit crisis is just how readily defaults can spread from bank to bank, city to city, and nation to nation. This is because a large proportion of credit dependencies cross these boundaries, and so ripples have many, many ways to propagate from bank to bank, city to city and nation to nation.

If loans were mainly made within the same banks, the same cities and the same countries, then a credit crunch in the USA probably would have little effect on credit networks in Europe, Asia and the rest of the world. There’s a very strong argument for stronger local economies with weaker international ties.

You could coin the design mantra: banks that fail together belong together.

Similarly, if the trees in forests were arranged in small, cohesive clusters that are mostly isolated from the other clusters, then forest fires would be less likely to spread beyond those clusters.

And how we package up our code, into methods, into classes and into release-managed packages (e.g., .NET assemblies) can help us to localise ripples. Highly dependent code should be packaged together into cohesive, loosely coupled units. So, for example, methods that access fields should be packaged in the same classes, and classes that depend on each other should be organised into the same packages.

The equivalent software design mantra might be: things that change together belong together.

Whether these “things” are individual lines of code, or data and behaviour, or class features, or classes themselves, or packages of classes, or subsystems, or systems inside systems of systems (e.g., service-oriented architecture). If changing one of them might force us to change another, then we should strive for them to be packaged together so we can limit the ripples from those changes.

But we cannot avoid having some dependencies cross boundaries. To remove duplication from code, for example, we must package the common elements into reusable units – methods, classes and packages. And to reuse these units of code, we must create dependencies on them. There’s no other way of doing it.

In those instances where boundary-crossing dependencies are unavoidable, we can apply one more strategy to help limit the size of ripples: we can make sure that, as much as possible, those dependencies go in the right direction.

Consider again our network of lenders and borrowers. If Paul lends money to Mary and Tom, and Peter lends money to Paul, which is the better situation for the network as a whole?

  1. Mary and Tom have a poor credit rating. Paul has a good credit rating. Peter has a good credit rating.
  2. Mary and Tom have good credit ratings. Paul’s credit rating is not as good. Peter’s is the worst.
This really takes some thought. We’re concerned with two distinct factors here when judging how big ripples might be: firstly, the probability that someone in the network will default first, and the probability that if they do default, then that will cause other dependent lenders to default on their loans.

Defaults can only propagate from borrowers to lenders. If someone lends money only –and borrows nothing – then defaults can only propagate towards them, because there’s nobody they can default on themselves. If someone only borrows – and lends nothing – then there is no way that anyone can default on them. Defaults cannot propagate towards them, only away from them.

If I’m a real credit risk, and therefore likely to default, and have lots of lenders depending on my repayments, then that’s a bad situation for all involved (especially if those lenders also have loan repayments to make).

If I’m squeaky clean and always pay my debts, then there’s no problem. Sure, if I did default it would be bad news, but I’m very unlikely to default in the first place.

So it’s best for the overall health of the network if borrowers are actually less of a risk then the people lending them money. In that scenario, most of the dependencies point towards the more dependable people.

Same goes for software designs: it’s better for the code as a whole if dependencies point towards things that are less likely to change. Simple, stable interfaces, for example, are far less likely to change than complex concrete classes. As a general guide, it’s better to depend on things that are more abstract and more stable.

By following these three strategies:

  • Keep dependencies to a minimum
  • Encapsulate dependencies inside organisational boundaries
  • Depend on things that are – well – more dependable
You will find that ripples tend not to spread as far and that the cost of changing your code should be significantly lower.

And, if you work in the financial markets, you might want to reconsider the wisdom of unchecked market growth, globalisation and sub-prime lending, of course.


Jason Gorman is an independent consultant and rabble-rouser, based in London, with 16 years’ software development experience. His web site offers guidance and advice on OO software development, architecture and design, and Agile practices to over 30,000 visitors every month.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Debugging is anticipated with distaste, performed with reluctance, and bragged about forever.” - Dan Kaminsky