By Brad Cross - 4 December 2008In the technical balance sheet, the cost of carry and cost of switching are proxies for cash flow.
When you take on technical liabilities, you incur cash flow penalties in the form of ongoing interest payments, i.e. going slow.
When you pay down principal on your technical liabilities, you incur cash flow penalties in the form of payments against principal. However, these cash flow penalties are of a different nature: they come in the form of paying down principal in the short term (going slower right now) in exchange for paying less interest (going faster as the principal is paid down).
The notion of going faster or slower shows the connection between cash flows and time. The cost of ongoing interest payments is an incremental reduction in speed, whereas the cost of payments against principal is an investment of time in exchange for an increase in speed. Restated, there is a trade off between cash flow penalties now (paying the cost of switching) for decreased cash flow penalties in the future (reducing the cost of carry).
Before we look further at these terms, we need to first introduce another concept: volatility. Volatility is the extent of change of a component, including maintenance and new development. The more work there is to do in an area of the code that has high technical liability, the greater the cost to complete that work. You can determine the volatility of a component by looking at previous project trackers, defects logs, the raw size of the component, source control history, and backlog work items.
Cost of carry is the sustained cost of servicing your liabilities. It represents the decrease in speed and corresponding increase in time that it takes for developers to complete tasks due to technical debt. Volatility is a major component of cost of carry: the liabilities in a high volatility component are the liabilities with the highest cost of carry across your application.
Within a component, your cost of carry is the time spent on production issues, maintenence issues, adding new code to change or extend the system, as well as testing and debugging time. The cost of carry for an indebted component also includes costs from its effects on dependent components. In other words, some dependent components are more difficult to productionize, maintain, change, extend, test and debug becasue of their dependency on an indebted component.
Cost of switching is the cost associated with your mitigation strategy. When indebted code is an impediment to sustaining and evolving the functionality of your project, your mitigation strategy is how you go about transitioning away from indebted code in order to reduce or eliminate the impediments. This can entail completely replacing a component with another component, partially replacing a component, or simply small incremental refactorings that accumulate over time. Switching costs are impacted by the size and scope of the component you are replacing, the time you have to spend to find and evaluate commercial and open source alternatives, time spent on custom modifications to the replacement, and time spent on migrating infastructure - for instance, migrating or mapping user data after switching to a different persistence technology.
The effect of dependencies and encapsulation
Both switching costs and cost of carry are proportional to encapsulation and dependencies.
If a technical liability is poorly encapsulated, other components that depend on the indebted component also incur higher switching and carry costs. This is an extremely important concept to understand, and one of the reasons why encapsulation is one of the most important ideas in the history of computing. (If you do not understand or track the dependence structure of your project, you can use a tool such as jDepend or nDepend.)
It is also important to understand that the impact of dependencies on switching costs and cost of carry can be transitive. This means that poor encapsulation of one high-liability component can have profound impact across not only its direct dependents, but also transitive dependencies that are several steps from the indebted component in the dependency graph.
If a high liability component is not highly volatile, and the costs of switching are high, then making the effort to substitute the component is a low priority. However, dependencies can overrule this judgment because indebted components can impact your cash flows by trickling down into other components that are highly volatile.
A component may be replaceable with an open source counterpart, but if the use of the component to be replaced is not properly encapsulated, then the switching cost can be quite high. It is important to note that in this case the cost of switching is higher because poor encapsulation itself is a technical liability. Often you have to spend a lot of time paying down that debt before you can consider replacing a component.
In the Technical Liabilities article, we talked about benign versus malignant risk. Benign risks are well encapsulated risks in low volatility code. Malignant risks are poorly encapsulated risks, risks in high volatility code, or both. Clearly, the cost of carry on benign risks is far lower than the cost of carry on malignant risks.
Making trade-off decisions
You can evaluate different cost of carry vs. cost of switching scenarios by taking the present value of each cash flow stream for each scenario. You can then compare the net present value of the different scenarios and determine the highest-value course of action. Switching is attractive when the present value of the stream of future cash flows from cost of carry is higher than the present value of the stream of future cash flows from cost of switching.
The next article in this series will be a field guide for execution that is dedicated to details and examples of using this mental model to make trade off decisions.
About Brad Cross: Brad is a programmer.