TODO hygiene
A TODO is a promise the code makes to itself, and most are never kept. They rot into archaeology that no one dares delete and no one acts on. A TODO earns its place only when it names who owns it, which ticket tracks it, and when it expires.
Method
- Require a triple on every TODO: owner, ticket, expiry date. Format it
so it reads at a glance:
TODO(asmith, PROJ-4821, 2026-09-01): drop the v1 fallback once all clients ship. A bare// TODO: fix thisgets resolved or deleted in the same change, never merged. - Delete instead of deferring when the fix is small. Ask whether the work is cheaper than the comment plus the ticket plus every future reread. Often it is: resolve it now and write no marker at all.
- Grade by marker word.
TODOfor deferred work,FIXMEfor known-broken code,HACKfor deliberate ugliness with a stated reason. Do not invent a fourth. A mixed vocabulary makesgrepuseless. - Point to a tracked item, not your memory. Every TODO that survives review links to an issue with an owner and a backlog slot. Comment-only TODOs are invisible to planning and outlive whoever wrote them.
- Sweep on a schedule. Run
grep -rn "TODO\|FIXME\|HACK" src/as a monthly chore or a CI report. Any marker past its expiry date fails the check: resolve it, re-date it with a reason, or delete it. - Never park a security or data-loss issue in a TODO. Those become a tracked ticket immediately or get fixed in the branch. A code comment is not an incident response.
Checks
- Does every TODO in the diff name an owner, a ticket, and a date?
- Can you
grepone marker word and find every deferred item of that kind? - Would the oldest TODO in the file survive you reading it aloud to the person named as its owner?
Boundaries
Where a linter already enforces a format (ESLint no-warning-comments, a
custom Semgrep rule), match its shape instead of imposing this one:
consistency beats preference. Throwaway spikes and prototypes that will
never merge are exempt.