Code as Liability

What Software Engineers Can Learn from Finance

Code as Liability
Photo by Joshua Sortino / Unsplash

There's something I came to realise about software development: every line of code you write is a liability until it proves otherwise. It's equivalent to financial liabilities in accounting.

I've been thinking about this parallel between code and financial liabilities for a while now, and the more I dig into it, the more useful it becomes as a mental model for making better engineering decisions. Let me explain.

The Balance Sheet of Software

In accounting, a liability is an obligation - something you owe, something that requires future resources to maintain or settle. Assets, on the other hand, generate value. The distinction seems simple, but here's the thing: when you first take on a liability, it often looks like you're getting something valuable. A loan gives you cash. A lease gives you office space. But these aren't assets - they're commitments that will cost you over time.

Code works the same way. When you launch a feature, it feels like you've created an asset. You've added functionality, solved a problem (hopefully), and made users happy. But what you've actually done is taken on an obligation. That code now needs to be:

  • Maintained as dependencies update
  • Understood by future engineers (including future you)
  • Modified when requirements change
  • Debug when edge cases emerge
  • Secured against new vulnerabilities
  • Tested to prevent regressions
  • Documented so it's not a black box

Every single line of code you write is a promise that you or someone else will pay these costs. The code doesn't become an asset until the value it generates exceeds the cost of these obligations, and the uncomfortable truth is that a lot of code never crosses that threshold.

The Interest on Technical Debt

Financial liabilities accrue interest. Miss a payment, and you owe more. Let the principal sit too long, and the interest compounds. Technical debt works the same way.

When you take a shortcut - skip the tests, hard-code a value, copy-paste instead of abstracting - you're not just creating debt. You're creating debt that compounds. That hard-coded value gets referenced in three other places. Those missing tests mean you're afraid to refactor. That copy-pasted code diverges, and now you have to maintain multiple versions of the same logic.

When teams lack standardised tooling, automated testing, and deployment pipelines, they're essentially taking on high-interest technical loans.

I've seen codebases where the "interest payments" on technical debt consumed 60-70% of engineering capacity. Teams spend most of their time working around old decisions instead of building new value. That's what happens when you let liabilities compound without paying them down.

You should pay off high-interest debt first. Refactor the code that changes most often. Add tests to the modules that break most frequently. Abstract the patterns you keep repeating. These aren't nice-to-haves - they're interest payments that prevent your liabilities from spiralling out of control.

Depreciation and Code Decay

In finance, assets depreciate. A piece of equipment loses value over time through wear and tear. In software, code depreciates too, but for different reasons.

Your code doesn't wear out from use - it decays from context drift—the libraries you depend on release new versions. Security vulnerabilities get discovered. User expectations evolve. Browser APIs change. What was modern and maintainable three years ago is now a liability that's expensive to keep running.

This is why "if it ain't broke, don't fix it" is terrible advice for software. That code isn't broken yet, but it's experiencing bit rot - a gradual decay in value and functionality as the world changes around it. Its value is depreciating every day, just like financial assets on a balance sheet. The longer you wait to update it, the more expensive the update becomes.

I learned this lesson the hard way, working on a platform that hadn't updated its frontend framework in five years. By the time we decided to modernise, the gap was so large that we couldn't do it incrementally. We had to do a partial rewrite, which meant months of work. That's what happens when you ignore depreciation.

Minimising Liabilities: The Art of Not Writing Code

Here's the thing about financial liabilities: the best way to manage them is to not take them on in the first place. Don't borrow money you don't need. Don't lease space you won't use. Don't commit to obligations that don't generate clear returns.

The same principle applies to code. The best code is the code you don't write.

Before you build something, ask yourself:

  • Can I use an existing library instead of rolling my own?
  • Can I configure this instead of writing code for it?
  • Can I solve this with a more straightforward approach that requires less code?
  • Do we actually need this feature, or are we building it because we can?

Every line of code you don't write is a liability you don't have to maintain. This isn't about being lazy - it's about being strategic. Your engineering capacity is finite. Every hour spent maintaining unnecessary code is an hour you can't spend building something valuable.

We've reduced the frontend codebase at Addepar by ~20% by deleting unused features and dead code, consolidating duplicated logic, and replacing custom implementations with well-maintained libraries. We ended up with faster builds, fewer bugs, easier onboarding, and more time to focus on what actually matters.

Converting Liabilities to Assets

So, when does code stop being a liability and become an asset? When it generates more value than it costs to maintain.

This is where metrics matter. You need to know:

  • How much time does this code save users?
  • How much revenue does this feature generate?
  • How many support tickets does this prevent?
  • How much faster can we ship because this infrastructure exists?

If you can't answer these questions, you don't know if your code is an asset or a liability. And if you don't know, it's probably a liability.

One of the most effective strategies for converting liabilities into assets is leveraging open-source software. Each line of code you don't have to write is a liability you never take on. Well-established OSS libraries represent pre-amortised liabilities—someone else has already absorbed the initial development cost and much of the maintenance burden.

Platform Engineering provides excellent frameworks for this conversion process. The core thesis is that well-designed platforms transform what would otherwise be liabilities (custom deployment scripts, bespoke monitoring solutions, etc.) into organisational assets that generate returns across multiple teams. By centralising and standardising these capabilities, the maintenance cost is amortised while the value compounds with each new team or service that leverages them.

Practical Advice for Engineers and Leaders

If you're an individual contributor:

  • Write less code. Solve problems with the simplest possible approach.
  • Pay down technical debt in the areas you touch most often.
  • Delete code aggressively. If you're unsure whether something is in use, instrument it and find out.
  • Document why you made decisions, not just what you built. Future maintainers need context.
  • Consider the maintenance cost, not just the initial implementation time.

If you're a tech leader:

  • Make code deletion a celebrated activity, not just code creation.
  • Build time into roadmaps for paying down technical debt. Treat it like the interest payment it is.
  • Measure the cost of maintaining code, not just the cost of building it.
  • Create a culture where "we don't need to build this" is a valid and respected answer.
  • Regularly review your codebase and sunset features that aren't generating returns.

Quantifying Code Liabilities: A Practical Model

Understanding when code transitions from liability to asset requires more than intuition—it demands quantifiable metrics (just like in accounting). The accompanying spreadsheet model provides a framework for making these calculations concrete and actionable.

The model centres on five key metrics that capture the total cost of code ownership. Initial Code Liability combines lines of code, complexity scores, and technology risk factors to establish baseline development costs. This isn't just about time invested, but the ongoing obligation created by each line written.

Maintenance Cost Over Time applies an annual percentage of the initial investment, typically ranging from 15% to 25% for well-architected code to 50% or more for legacy systems. Technical Debt Accumulation compounds annually like interest, reflecting how deferred improvements multiply maintenance burdens. Meanwhile, Value Generation quantifies the business benefits—revenue enabled, costs saved, or productivity gained.

The critical insight emerges in the break-even analysis. Most code begins in a deeply complex and liability-prone territory, with high upfront costs and mounting technical debt. The transition point occurs when the cumulative value generated exceeds the total costs (initial, maintenance, and technical debt). For typical enterprise applications, this occurs between 18 and 36 months, but varies significantly based on the business context and code quality.

To use the model effectively, start with conservative estimates and adjust based on historical data. A complexity score of 7 or higher often suggests that architectural decisions may be worth reconsidering. Maintenance factors above 30% suggest immediate refactoring needs. The model's power lies not in precision, but in making implicit costs explicit and enabling data-driven decisions about when to build, buy, or sunset code assets.

Final Thoughts

Code is a liability until proven otherwise. Every line you write is a commitment to future maintenance, future understanding, future modification. The goal isn't to avoid writing code - it's to be intentional about which liabilities you take on and to ensure they generate enough value to justify their cost.

Think like a CFO, not just an engineer. Minimise unnecessary liabilities. Pay down high-interest debt. Let low-value assets depreciate and replace them before the cost becomes unbearable. Measure returns and divest from code that isn't paying off.

Your codebase is a balance sheet. Ensure that your assets exceed your liabilities.