Tech Debt and Refactoring

The Quiet Cost of Skipping Type Safety

Skipping type safety is a decision that looks free at the start and reveals its cost over eighteen to twenty four months. The symptoms are not dramatic. They are slow: bugs that require a debugger to trace, refactors that take three times the estimate, and engineers who spend more time reading code than writing it. I have cleaned up the aftermath of this decision enough times that I no longer see it as a style preference.

September 14, 2025 · 13 min read
Tech Debt and Refactoring

The Engineering Migration: Patterns That Work

Engineering migrations (moving from one database schema, one service architecture, or one infrastructure platform to another) fail most often because they are treated as single, isolated events rather than incremental processes. The patterns that work share a common structure: make the old and new systems coexist, move traffic incrementally, validate at each step before proceeding, and maintain the ability to roll back until confidence is high. The pattern is applicable to schema changes, service extraction, and platform migrations.

September 12, 2025 · 12 min read
Tech Debt and Refactoring

The Architecture Decision Record: A Lightweight Discipline

An Architecture Decision Record is a short document that captures a significant technical decision, the context that drove it, the alternatives considered, and the reasoning behind the choice. ADRs are not design documents. They are decision logs. The value is not in the writing. It is in the future conversation that does not have to happen because the answer is already written down.

September 11, 2025 · 12 min read
Tech Debt and Refactoring

The Mock Versus Real Service Debate

The mock versus real service debate in software testing concerns whether tests should use mocked versions of external dependencies (databases, third party APIs, queues) or real versions. The answer depends on the test type and the purpose of the test: unit tests test isolated logic and should mock everything external; integration tests test how components interact and should use real implementations of internal dependencies but can mock external APIs; end to end tests test the full user flow and should use real services wherever practical. The failure mode that causes the most production bugs is mocking too aggressively in integration tests, producing tests that pass against mocks but fail against real implementations.

September 7, 2025 · 12 min read
Tech Debt and Refactoring

The Test Pyramid for SaaS: Unit, Integration, End to End

The test pyramid describes the ideal ratio of unit tests to integration tests to end to end tests in a healthy test suite. Unit tests form the base, integration tests sit in the middle, and end to end tests sit at the top. Most SaaS teams invert this unintentionally, building too many brittle end to end tests and too few integration tests. Understanding why the pyramid is shaped the way it is changes how you allocate testing effort.

September 3, 2025 · 12 min read
Tech Debt and Refactoring

The Tech Debt Negotiation: How Engineers Should Talk to Founders About It

The tech debt negotiation is the conversation where an engineer explains to a founder why the team needs time to fix something that already works. Most engineers lose this conversation because they speak in technical terms to someone who thinks in business terms. I have run this negotiation dozens of times. The engineers who win it stop arguing about code quality and start arguing about cost.

August 31, 2025 · 12 min read
Tech Debt and Refactoring

The Tech Debt Ledger: A Discipline Worth Keeping

A tech debt ledger is a living document that tracks the shortcuts a codebase has accumulated, what each one costs per quarter, and who owns the decision to pay it down. I keep one on every project I run. It makes the invisible visible, turns a vague sense of dread into a prioritized list, and gives engineers a way to talk to founders without sounding like they are making excuses.

August 31, 2025 · 12 min read
Tech Debt and Refactoring

Why Most Rewrites Fail

Most rewrites fail not because the engineers are incompetent but because the work expands to absorb every known problem with the old system, the timeline slips past the point where the business can wait, and the new code turns out to have its own edge cases that only production use reveals. I have seen this pattern across dozens of teams. Understanding why it happens is the first step to avoiding it.

August 30, 2025 · 13 min read
Tech Debt and Refactoring

The Strangler Fig Pattern: Replacing Legacy in Stages

The strangler fig pattern replaces a legacy system incrementally by routing traffic surface by surface to a new system that grows around the old one. The old system continues running until enough surfaces have been migrated that it can be retired. I use this pattern on every large migration where a big bang cutover would be too risky and a direct rewrite would take longer than the business can sustain.

August 29, 2025 · 12 min read
Tech Debt and Refactoring

When to Refactor and When to Rewrite

Refactoring improves the internal structure of code without changing its observable behavior. Rewriting replaces the code entirely with a new implementation. The decision between them is not about code quality. It is about cost, risk, and what the existing code knows that you do not. I lean heavily toward refactoring, and I push for rewrites only when I can show the math that justifies them.

August 28, 2025 · 12 min read
Tech Debt and Refactoring

The Tech Debt Audit: A Two Day Process

A tech debt audit is two days of structured investigation that produces a ranked list of the highest cost problems in a codebase. I run this on every new engagement before recommending any refactor or rewrite. The goal is not a comprehensive catalogue. The goal is a short list of the things that are actively slowing the team down or creating risk right now.

August 27, 2025 · 12 min read