devShakib

Technical Debt I Take On Purpose (and Log Like a Loan)

How to manage technical debt at a startup: treat deliberate debt like a loan. Log principal, interest, and repayment triggers so it never becomes a rewrite under fire.

Most engineers talk about technical debt like it's mold. Something that grows in the dark, that you're vaguely ashamed of, that you promise to "clean up later." That framing is useless. It makes debt a moral failing instead of a decision, and decisions you're ashamed of are decisions you stop tracking.

I run engineering at a startup in Dubai. We ship fast, we're small, and we take on technical debt every single sprint on purpose. The difference between a team that survives that and a team that drowns in it isn't willpower. It's bookkeeping. I treat deliberate technical debt exactly like a business loan: I write down the principal, I estimate the interest, and I schedule repayment before the interest eats the product. This post is how I actually manage technical debt in practice — the mental model, the loan agreement, the ledger, the repayment triggers, and the conversations with non-engineers that keep the whole thing honest.

Deliberate Technical Debt vs. Rot You Drifted Into

There are two things people call "technical debt," and conflating them is the original sin of every debt conversation.

Debt is a shortcut you took knowingly, for a reason you could state out loud, with a rough idea of what it'll cost to unwind. "We're hardcoding the pricing tiers because we'll have real data in six weeks and any schema I design today is a guess." That's a loan. You borrowed time against future rework.

Rot is what accumulates when nobody decided anything. Nobody chose the God-object UserService with 4,000 lines. Nobody chose the three slightly different ways the app formats a date. Nobody chose the copy-pasted null check that quietly diverged into four subtly different behaviors. That's not debt, it's erosion. There's no counterparty, no principal, no due date. You can't refinance rot because you never signed for it.

The test I use is brutal and simple: can you name the shortcut, the reason, and the trigger to fix it? If yes, it's debt and it goes in the ledger. If you can't, it's rot, and rot only gets fixed by changing how the team writes code — better review, linters, a shared style, tighter module boundaries — not by scheduling a "cleanup sprint" that never comes.

I only defend the first kind. Deliberate technical debt is a legitimate financing instrument for a startup that's racing to find out whether anyone wants the thing. Rot is just a mess you're pretending is strategy. The moment you start calling erosion "debt," you've given yourself permission to stop fixing your process, and the process is the only thing that actually generates rot.

Writing the Loan Agreement at the Moment You Borrow

When I take on debt intentionally, I write it down at the moment I take it, not later. Later never happens. I learned this the expensive way: for two years I told myself I'd document shortcuts in the retro. I never did. The retro is for velocity charts and hurt feelings, not for a shortcut you already stopped thinking about. So now the commit that introduces the shortcut carries a structured comment, and it takes about ninety seconds.

// DEBT(#142): Hardcoded subscription tiers instead of a Firestore-backed// pricing config.// Borrowed: ~2 days of schema + admin UI work.// Reason: pricing will change 3-4 times before PMF; designing a flexible//   schema now is guessing. Ship the paywall, learn the real tiers.// Interest: any pricing change = a code deploy + review + release (~2h each).// Due: when we change pricing a 3rd time, OR before we onboard a//   self-serve customer who can pick a plan. Whichever comes first.const _tiers = [  Tier(name: 'Free', priceAed: 0, maxProjects: 3),  Tier(name: 'Pro', priceAed: 49, maxProjects: 50),];

A loan agreement has four fields, and I insist on all four:

The comment is the fast copy. The real record lives in the ledger (more on that below) so it survives even after the code around it moves, gets refactored, or the file it lived in disappears entirely.

Why the Comment Alone Isn't Enough

A code comment has one fatal weakness as a debt record: it only exists where the shortcut is. If you split that file, delete the function, or move the logic into a package, the loan vanishes with it — and you've now got outstanding debt with no paperwork. That's exactly how a deliberate shortcut quietly decays into rot. The comment is a signpost for the next engineer who touches this line. The ledger is the accounting system that outlives the code. You need both, and they serve different readers.

Interest Rates: Pricing a Shortcut Per Month

Principal is easy — it's the time you didn't spend. Interest is the hard, valuable part, because interest is what tells you which debts are dangerous and which you can carry for years.

Interest is the recurring cost of living with the shortcut. I estimate it in one of three units:

The thing most people miss: debt with low interest can be carried almost forever, and debt with compounding interest must be repaid fast. These are completely different instruments even if the principal is identical. Two shortcuts that each saved you two days are not the same loan if one costs nothing to carry and the other doubles its cost every quarter.

A hardcoded config that changes twice a year is a 0%-APR loan. Ignore it. Carry it for three years. Who cares.

A missing database index on a table that doubles every quarter is a payday loan. The interest compounds against your growth curve. The query that's 200ms today is 2 seconds in six months and an incident at the worst possible time — usually right after a marketing push, when the traffic you fought for lands on the one screen you cheaped out on.

A Worked Example: Client-Side Fan-Out That Compounded

On a recent project we shipped an activity feed that fanned out reads on the client — roughly 40 Firestore reads to paint one screen. Fine at launch with 30 users. The interest was "reads scale linearly with feed length and user count." We logged it as high-interest and set the trigger at 1,000 daily active users. We hit the trigger, spent three days moving to a denormalized feed collection, and cut it to 3 reads per screen.

The math is what makes this a loan and not a vibe. At 30 users, 40 reads per screen open is background noise. At 1,000 DAU opening the feed a few times a day, you're into hundreds of thousands of reads a day just to render one view — and every one of them is billed, latency-bound, and multiplied again if the feed gets longer. That's the compounding: the read count grows with both the user count and the feed length, so the cost curve bends upward faster than your revenue does. If we'd treated it as low-interest and drifted, that's a rewrite under fire during a growth spike instead of a scheduled three-day repayment we chose the timing of.

The lesson generalizes: any shortcut whose cost is a function of a number you expect to grow — rows, users, requests, teammates — is compounding debt, and compounding debt gets a trigger, not a "we'll get to it."

The Debt I Gladly Take Pre-PMF and Refuse After

Product-market fit is the line that changes the interest rate on everything. Before PMF, your biggest risk is building the wrong thing well. After PMF, your biggest risk is the right thing falling over. So the debt I take flips hard across that line, because the same shortcut has a completely different interest rate depending on which side of PMF you're standing on.

Gladly take before PMF:

Refuse even before PMF — these aren't debt, they're negligence:

The rule: I'll borrow against my future time. I won't borrow against my users' trust or my data's integrity. Those loans have no repayment plan. The distinction is whether the worst case is recoverable. Bad code is always recoverable — you can refactor it, delete it, rewrite it under pressure if you have to. A leaked credentials table or a truncated production collection is not recoverable, and no amount of engineering heroics next quarter buys it back.

A Technical Debt Ledger Non-Engineers Can Read

The code comment is for engineers. But technical debt is a business decision, and business decisions need to be visible to people who don't read Dart. So the real record is a flat table — a Firestore collection with an admin view, though a shared sheet works just as well when you're starting out. Deliberately low-tech: if the ledger is a pain to update, nobody updates it, and an abandoned ledger is worse than none because it lies.

| ID | What we skipped | Borrowed | Interest | Trigger to repay | Status |

|----|-----------------|----------|----------|------------------|--------|

| #142 | Hardcoded pricing tiers | 2d | ~2h per pricing change | 3rd pricing change or first self-serve signup | Open |

| #150 | Client-side feed fan-out | 4d | Grows with DAU × feed length | 1,000 DAU | Repaid |

| #163 | Manual customer onboarding script | 5d | ~30min per new customer | 15 customers or first churn from onboarding pain | Open |

| #171 | No staging environment | ongoing | Rises with team size | 3rd engineer hired | Open |

Four things make this ledger actually work:

The Ledger as an Onboarding Document

There's a second-order benefit I didn't expect: the ledger is the fastest way to explain "why is the code like this?" to a new engineer. Instead of them discovering the hardcoded tiers, assuming you're an idiot, and quietly resenting the codebase, they read one line — skipped on purpose, here's the reason, here's when we fix it — and they get it. A visible technical debt ledger converts what looks like sloppiness into what it actually is: a series of dated, reasoned decisions. That's a morale tool as much as a planning tool.

Repayment Triggers That Force the Fix Before the Rewrite

Debt kills startups when repayment is optional, because repayment is never the most urgent thing until it's the only thing. The fix is to make repayment automatic by tying it to a condition you'll actually notice. Vague intentions lose to shipping every time.

Good triggers, roughly in order of how much I trust them:

I wire triggers into things I already look at. The 1,000-DAU line isn't a note in a doc I'll never reopen — it's an alert. When the metric crosses, the ticket opens automatically and lands in the next sprint. Remove the human judgment call in the moment, because in the moment there's always something more urgent than paying off a loan.

The One Rule I Never Break: No Refinancing a Payday Loan

I hold one line hard: you don't take a new high-interest loan while a high-interest one is outstanding in the same area. Don't refinance a payday loan with a second payday loan. If the feed fan-out is already compounding and someone proposes another growth-scaling shortcut in the same subsystem, the answer is no — pay down the first one, or don't take the second. That's the exact spiral where debt stops being a tool and becomes the reason for the rewrite. One high-interest loan in a subsystem is leverage. Two stacked on top of each other is how the subsystem becomes the thing nobody wants to touch, which is how it becomes the thing that gets thrown away.

Explaining the Balance Sheet to Your Co-Founder and Board

Non-technical stakeholders don't need to understand denormalization or Firestore read pricing. They understand loans. So I speak in loans, and it lands every time.

With my co-founder, the conversation is a trade, framed in their currency — time and money:

"We can ship the paywall Thursday if I hardcode the tiers. That saves two days now. The cost is that changing pricing means a code release each time, about two hours, until we build the real config. I think that's a good trade for the next two months. Agree?"

That's it. Principal, interest, term, and an explicit yes. Now it's a shared decision, not something I did in the dark that they discover three months later when velocity mysteriously tanks. The worst technical debt conversations are the ones that only happen when something's already on fire — and the way you avoid those is by having the cheap version of the conversation up front, in ninety seconds, before you write the shortcut.

For a board or investors, I zoom out to the balance sheet:

The reframe that works: some technical debt is healthy leverage. A startup carrying zero technical debt is either lying or moving too slowly to survive. Carrying it blind is how you die. Carrying it on a ledger, with interest priced and repayment scheduled, is just how capital-efficient teams operate. Boards understand leverage. Speak their language.

Key Takeaways