I spent some time recently exploring a large C codebase — over half a million lines, fifty-plus modules, a serious engine with years of work behind it. My task was simple: figure out what features to add next.

I expected to spend hours reading architecture docs, tracing call graphs, mapping module dependencies. Instead, I found the answer in about ten minutes.

The TODOs told me everything.

The TODO as a Map

Most codebases treat TODO comments as technical debt markers — things to feel guilty about, things to sweep under the rug during code review. “We’ll get to it later” is the implied subtext, and “later” usually means “never.”

But this codebase used them differently. Each TODO was specific, contextual, and actionable:

/* TODO: Integrate with LrgAudioManager to start engine loop */
/* TODO: Implement YAML loading using yaml-glib */
/* TODO: Push effect to effect stack and resolve */

These aren’t vague reminders. They’re breadcrumbs. They tell you exactly what the developer intended, what interface they expected to use, and what system needs to exist before this piece can be completed.

What Good TODOs Reveal

When I categorized the TODOs I found, patterns emerged immediately:

Integration gaps — Systems that exist independently but aren’t wired together yet. The audio manager exists. The vehicle audio system exists. They just don’t talk to each other. The TODO marks the exact function where the handshake should happen.

Serialization holes — Objects that can be created in code but not loaded from data files. The TODO says “implement YAML loading using yaml-glib” — not just “add serialization” but the specific library and format expected.

Stub implementations — Functions that return placeholder values with a TODO explaining what the real implementation should do. These are deliberate architectural decisions, not laziness.

Blocked dependencies — Work that can’t proceed until another API exists. “Implement when lrg_engine_get_worlds() is available” tells you both what’s blocked and what needs to happen to unblock it.

The Anti-Pattern

Compare this to the TODOs you usually see:

// TODO: fix this
// TODO: refactor
// TODO: handle edge cases
// TODO: make this better

These are noise. They don’t tell you what “this” is, what the refactoring target looks like, which edge cases matter, or what “better” means. They’re the equivalent of writing “do something” on a sticky note and putting it on your monitor.

The difference is intent. A good TODO is written for your future self — or for a completely different developer who’s never seen this code before. It answers: what needs to happen, what tools to use, and what’s blocking progress.

TODOs as Feature Roadmap

Here’s the thing that surprised me most: the TODOs in that engine effectively were the roadmap. I could look at the codebase and immediately tell you:

  • The deckbuilder combat system needs a context object before card effects can resolve
  • All scene transitions are logically complete but need rendering hookup
  • The settings system stores preferences but doesn’t apply them to the engine
  • The MCP integration tools are waiting on a specific engine API

No Jira board, no project management tool, no spec document gave me this picture. The TODOs did, because they lived in the code, right next to the context that explains them.

Write TODOs Like You’re Leaving a Trail

If you’re going to leave a TODO, make it count. Include:

  1. What needs to happen — the specific action, not a vague wish
  2. How it should be done — the library, pattern, or approach to use
  3. Why it’s not done yet — the dependency, blocker, or decision that’s pending

A TODO that meets all three criteria isn’t technical debt. It’s a blueprint. It’s the most honest documentation your codebase has, because it lives right where the work needs to happen and it admits exactly what’s missing.

The best codebases I’ve explored aren’t the ones with zero TODOs. They’re the ones where every TODO teaches you something about the architecture.