← All Essays

Every Line of Code is a Liability (And Why Your Refactor is Killing Revenue)

Every line you write is a tax on the company's future. The functionality is the asset - the source code required to deliver it is the ongoing invoice.

Code is a liability. Every line you write is a tax on the company’s future. The functionality is the asset. The source code required to deliver it is the invoice.

The Asset Delusion

Walk into any engineering team and ask someone to describe a codebase they’re proud of. They’ll tell you about elegant abstractions, test coverage percentages, a clever domain model. They’ll talk about it the way a collector talks about vintage hardware - something valuable, something to be preserved and admired.

That framing is wrong, and it costs companies money every day.

The functionality your code provides is the asset. The source code itself is the liability. It doesn’t matter how clean it is. The moment you write a line of code, you’ve made a promise: someone will read this, run this, debug this, update this, and eventually delete this. Every one of those future acts has a price tag. The code you’re so proud of is an ongoing invoice to your business.

Think about it in physical terms. A retailer’s inventory is not an asset - it’s a liability sitting on shelves, depreciating, requiring climate control, insurance, and labor to manage. The asset is the sold product. The inventory is the cost of producing that product. Code is no different. Your codebase is not IP in the venture-capital sense. It is warehouse space filled with obligation.

This distinction sounds academic until you watch a company spend eighteen months maintaining a custom-built user authentication system that they could have replaced with a $12-per-month SaaS product. The engineers who built it were proud of it. The CFO writing the payroll checks was not.

The Maintenance Tax and Digital Rubble

Every line of code you write is a vote for future work. Not a maybe. A guarantee.

The tax gets paid in security patches for a library nobody remembers adding. It gets paid in the four-hour debugging session tracing a null pointer through code written three engineers ago. It gets paid every time a new hire needs two weeks of onboarding just to understand the folder structure. The code doesn’t collect dust - it actively demands attention.

Now accelerate that problem by an order of magnitude. AI coding tools let a small team generate in a day what used to take a week. That sounds like productivity. What it actually produces is digital rubble - vast quantities of plausible-looking code that still needs to be read, understood, tested, maintained, and eventually replaced. The output velocity is up. The ownership cost is the same as always. A team of six can now generate a codebase that previously required a team of forty to maintain, and they’ll have no idea they’ve done it until eighteen months later when every sprint is half firefighting.

The senior engineers who’ve been through this before know the feeling. You join a codebase and immediately sense the weight of it - not the good, solid weight of well-considered architecture, but the dense, murky weight of accumulated decisions, each made with good intentions, none made with awareness of the long-term cost. You’re not working in a codebase. You’re working in a debt schedule.

The teams that understand this don’t celebrate code generation. They celebrate code avoidance. The best pull request is the one that deletes five files and closes three tickets. The team meeting nobody calls heroic is the one where someone says, “We can just use the platform’s built-in widget for this and skip the sprint entirely.”

Refactoring Has a Price Tag Too

The refactor feels like progress. It looks like diligence. It is often neither.

A developer decides the service that handles invoicing needs to be restructured. The existing code works - it’s messy, sure, accumulated some debt, but it ships invoices without error. Two weeks of refactoring later, the service is architecturally sound. It’s also two weeks later. The feature the sales team promised to close a deal is still sitting on the backlog. The deal fell through.

That refactor cost the company more than the two weeks of engineering time. It cost a customer and the revenue attached to them. But on the developer’s performance review, it looked like technical rigor.

This is the trap: the cult of clean code gives you a language to describe the work you want to do - pay down technical debt, improve maintainability, reduce coupling - without ever forcing you to price it against the work you’re not doing. Nobody asks “what is this refactor costing us in delayed features?” because that question feels offensive. Technical excellence shouldn’t have to compete with revenue. Except it does, every day, and pretending otherwise doesn’t make the competition disappear - it just means you’re losing it without knowing.

Every engineering decision is a resource allocation decision, whether you treat it that way or not. When you can articulate exactly what the refactor unlocks - it will cut our on-call incidents by half, it will let us add new payment providers without touching existing logic - you have a real argument. When the argument is “the current code makes me feel bad,” you’re spending organizational capital on an aesthetic preference.

The question isn’t whether clean code matters. It’s whether this refactor matters more than what it’s displacing.

And once you’re in the habit of asking that question honestly, the next one follows naturally: does this code need to exist in your codebase at all?

Outsourcing the Liability

There is a recurring conversation in engineering teams that sounds like a debate about architecture but is actually a debate about identity.

The question is whether to build something custom or buy an existing solution. The technical arguments for building are real: more control, better fit for specific requirements, no dependency on an external vendor. They’re also usually not the actual reason the team wants to build. The actual reason is that building is interesting. Buying is boring. Custom code feels like engineering. Plugging in a SaaS feels like settling.

Run the numbers instead of the feelings. A custom-built notification system takes eight weeks to build and two engineers to maintain in perpetuity. You’ll rebuild it twice as requirements evolve. You’ll page someone at 2am when it starts dropping messages under load. A SaaS alternative costs four hundred dollars a month and ten lines of integration code. The Total Cost of Ownership on the custom solution, priced at actual engineering salaries, is not close.

“But we have unique requirements.” Sometimes this is true. More often it means “our requirements are not that different from everyone else’s, but we haven’t looked hard enough at the available solutions.” The default assumption in software should be: someone has already solved this problem, productized the solution, and is willing to bear the maintenance burden for a monthly fee. Your job is to exhaust that option before deciding you need something bespoke.

Boring tech exists for a reason. Postgres is boring. S3 is boring. Stripe is boring. None of them fail in interesting ways. None of them require specialized knowledge to operate. Every hour your team spends maintaining an in-house equivalent of one of these tools is an hour not spent building the thing that actually differentiates your product.

The best engineers are aggressive about transferring the liability of maintenance onto vendors whose entire business is that maintenance. Every hour reclaimed from keeping an in-house tool alive is an hour spent on the thing your competitors can’t buy off the shelf.

Deletion Is the End Goal

The most respected engineers on the best teams are not the ones who wrote the most. They’re the ones who deleted the most.

Deletion is not a failure state. It’s the end goal. Every system, if designed well, trends toward simplicity over time - not because the problem gets easier, but because your understanding of it sharpens. The code you delete is code that was doing something the system no longer needs to do, or code that a better abstraction replaced, or code that was a hedge against a future that never arrived. Deleting it is the recognition that you were right about what matters.

The practical version of this is a discipline. Before adding anything, ask whether you can remove something instead. Before building a new service, ask whether an existing one can absorb the behavior. Before writing a new function, ask whether the requirement itself can be eliminated. Not as a reflex - some problems genuinely need new code. But as a default posture: the codebase is already paying a tax. Adding to it requires justification.

Teams that practice this end up with codebases that are genuinely smaller over time despite growing product surface area. They have fewer incidents because there is less to break. They onboard engineers faster because there is less to understand. They ship faster because the system resists sprawl. The codebase serves the product. It doesn’t consume the engineers who work on it.

If code is a liability - and it is - then the engineers who treat it that way are the ones who make the business cheaper to run. Not by cutting corners, but by refusing to accumulate what doesn’t need to be there. The best commit message isn’t a description of what was added. It’s a description of what was made unnecessary.