Managing Technical Debt from any: Governance, Not One-off Fixes
AI generated
<T>
type
TypeScript · Technical Debt · Governance
Managing Technical Debt from any
governance instead of one-off cleanup sprints

Technical debt from any usage does not disappear through a one-time cleanup day, it quietly keeps growing as long as no system stands behind it. CI budgets, mandatory tickets for exceptions, clear ownership and regular reporting turn any related technical debt into a measurable, governable topic instead of a lingering bad conscience for the team.

17 min readCI budget · ticket requirement · ownership · reportingTypeScript 5.x · ESLint · type-coverage

1. Why any-Related Technical Debt Is a Governance Topic

Technical debt from any is treated in many teams as an individual problem: a developer finds a spot with any, replaces it whenever convenient, and the topic is considered handled. This approach systematically fails, because new any occurrences appear just as fast as old ones disappear, as long as no overarching system controls how they arise.

A governance approach treats any related technical debt like financial debt: with a measured balance, a budget that must not grow, and clear accountability for repayment. That shifts the question from who has time to clean this up to how do we ensure the balance stops growing, which is a fundamentally different problem.

The difference becomes especially visible in growing codebases. A team without governance slowly loses track of how much any actually sits in the project, while a team with governance can name an exact number at any time and knows in which module technical debt concentrates.

2. Systematically Measuring any Occurrences Instead of Guessing

Every governance system starts with a reliable measurement. A simple grep for any produces false positives, for example in comments or strings, which is why specialized tools like type-coverage are better suited to calculate the actual share of type safe expressions in the codebase. This number forms the baseline against which every future change gets measured.

It matters to break the measurement down by module instead of only reporting a global number. A governance system that knows 80 percent of any occurrences concentrate in a single legacy module can prioritize far more precisely than one that only knows an undifferentiated total.


#!/usr/bin/env bash
# any-debt-report.sh — measure any-related technical debt per module
set -euo pipefail

echo "[REPORT] Overall type coverage:"
npx type-coverage --detail=false

echo "[REPORT] any-density by module:"
worst_module=""
worst_density="0"
for dir in src/*/; do
  module=$(basename "$dir")
  count=$(grep -r --include="*.ts" -o '\bany\b' "$dir" | wc -l)
  lines=$(find "$dir" -name "*.ts" -exec cat {} + | wc -l)
  density=$(awk -v c="$count" -v l="$lines" 'BEGIN { printf "%.2f", (l > 0 ? c / l * 100 : 0) }')
  echo "  $module: $count occurrences, ${density}% density"
  if (( $(echo "$density > $worst_density" | bc -l) )); then
    worst_module="$module"
    worst_density="$density"
  fi
done

echo "[REPORT] Highest density module: $worst_module (${worst_density}%)"
echo "[REPORT] Use this to prioritize ownership assignment, see section 5"

3. Enforcing an any Budget in the CI Pipeline

Once a reliable baseline exists, technical debt can be technically capped: a CI check compares the current any count against the baseline and fails the build as soon as the number rises. That prevents new technical debt from silently creeping into every pull request, while existing debt does not need to be fixed immediately.

This budget principle works psychologically differently from a mere recommendation. A developer who sees a failing build will actively search for a type safe alternative, while a plain warning in review is often overlooked or ignored. Governance here means the pipeline becomes the authority defending the budget, not a single reviewer.


#!/usr/bin/env bash
# any-budget-gate.sh — fail CI if any-count exceeds the agreed baseline
set -euo pipefail

BASELINE_FILE=".any-budget"
current=$(grep -r --include="*.ts" -o '\bany\b' src | wc -l)
baseline=$(cat "$BASELINE_FILE")

echo "[BUDGET] Current any count: $current"
echo "[BUDGET] Agreed baseline: $baseline"

if (( current > baseline )); then
  echo "[FAIL] any budget exceeded by $((current - baseline)) occurrences" >&2
  echo "[FAIL] add a ticket-referenced exception or remove the new any usage" >&2
  exit 1
fi

if (( current < baseline )); then
  echo "$current" > "$BASELINE_FILE"
  echo "[OK] baseline improved, new budget: $current"
fi

4. Requiring a Ticket for Every New Exception

An any budget alone prevents growth but does not explain why an exception arose. Governance additionally requires that every new any usage gets linked to a ticket documenting the reason, the planned fix and a target date. Without that coupling, technical debt stays invisible once the budget has been approved once.

In practice this requirement can be mapped directly into ESLint: a custom rule enforces that every any line carries a comment with a ticket reference in a fixed format. That makes the exception searchable and allows all open any tickets to be prioritized together in the backlog, instead of hunting for them scattered across the whole codebase.


// Governance rule enforced by a custom ESLint rule: any needs a ticket

// ALLOWED — matches the required pattern TICKET-<number>, EXPIRES-<date>
// TICKET-4821, EXPIRES-2026-10-01: vendor SDK types land in Q4
function parseVendorResponse(raw: any): VendorResponse {
  return raw as VendorResponse;
}

// REJECTED by CI — no ticket reference, no expiry date
function parseLegacyResponse(raw: any): LegacyResponse {
  return raw as LegacyResponse;
}

// REJECTED by CI — ticket present but no expiry date, still incomplete
// TICKET-5012
function parsePartnerResponse(raw: any): PartnerResponse {
  return raw as PartnerResponse;
}

interface VendorResponse { status: string }
interface LegacyResponse { status: string }
interface PartnerResponse { status: string }

5. Ownership: Who Owns Which any Hotspot

Technical debt without clear ownership becomes nobody's responsibility and therefore stays untouched. Governance means assigning a team or a person as owner to every module with high any density, similar to how a CODEOWNERS file governs code review. This ownership should not be understood as punishment, but as clear responsibility with a corresponding time budget.

An effective governance model couples ownership with a fixed share of sprint capacity, for example ten percent for technical debt reduction in one's own module. That prevents any reduction from always yielding to the first priority of a new feature and therefore effectively never happening.


# .github/ANY-DEBT-OWNERS — extends CODEOWNERS with debt-reduction duty
# format: <path>  <owning team>  <sprint capacity share>

src/legacy-checkout/    @team-payments   10%
src/reporting-export/   @team-analytics  5%
src/admin-tools/        @team-platform   5%

# reviewed and re-assigned quarterly in the architecture sync,
# capacity share feeds directly into each team's sprint planning

6. Escalation and Backlog Prioritization

Not every any exception deserves the same priority. Governance needs an escalation model that ranks any debt by risk: any in a core function with frequent changes deserves higher priority than any in a rarely touched peripheral module. That ranking should rest on the same metrics as the measurement itself, such as change frequency and bug history of the affected code.

A simple but effective tool is an automated reminder once a ticket with an any exception passes its target date. That prevents a planned, time-boxed exception from silently turning into a permanent one because nobody remembers the original target date anymore.

7. Reporting to the Team and Stakeholders

Governance requires transparency inward and outward. A monthly report summarizing the total any count, the distribution by module and the number of expired exceptions makes technical debt just as tangible for management as for the development team. That makes it easier to justify time budget for reduction, because progress is visible in numbers.

It matters not to frame this reporting as an accusation, but as a shared progress report. A team that sees the any count decline over several months experiences governance as an effective system, not as extra bureaucracy that only creates work without visible benefit.


{
  "//": "monthly-any-debt-report.json — generated summary for stakeholders",
  "period": "2026-07",
  "totalAnyCount": 214,
  "previousMonth": 251,
  "budgetLimit": 220,
  "expiredExceptions": 3,
  "topModulesByDensity": [
    { "module": "legacy-checkout", "count": 89, "owner": "team-payments" },
    { "module": "reporting-export", "count": 41, "owner": "team-analytics" }
  ],
  "trend": {
    "threeMonthAverage": 238,
    "direction": "declining",
    "onTrackForQuarterlyGoal": true
  },
  "openTicketsNearingDeadline": 2
}

8. The Culture Trap: Why Cleanup Sprints Alone Fail

Many teams try to solve any related technical debt with a one-time cleanup sprint. The number drops in the short term, but without a budget and ticket requirement it climbs back to the old level within a few months, because the actual cause, unchecked approval of new any usage, remains unchanged. A cleanup sprint treats the symptom, not the system behind it.

Governance differs because it controls the creation of new technical debt, not just paying down the existing balance. A team that only cleans up without limiting the inflow finds itself in an endless loop that produces more frustration than progress over time.

9. Ad Hoc Cleanup versus Governance Compared

The difference between one-off cleanup and a real governance system for any related technical debt shows most clearly when looked at over several months.

AspectAd Hoc CleanupGovernance SystemEffect
MeasurementGut feelingtype-coverage per module, regularlyObjective baseline for decisions
New any usageApproved without controlCI budget prevents growthBalance does not silently keep growing
ExceptionsUndocumentedTicket requirement with target dateTraceable and prioritizable
AccountabilityNobody specifically responsibleOwnership per module with time budgetReduction actually happens
Long term effectBack to old level after monthsSteady decline over timeSustained instead of short lived effect

10. Summary

Technical debt from any cannot be permanently solved with a one-time cleanup sprint, because without control over new exceptions the old amount gets reached again within a few months. A governance system made of reliable measurement, a CI budget, a ticket requirement and clear ownership treats the cause rather than just the symptom.

Regular reporting to the team and stakeholders makes progress visible and justifies the necessary time budget for continuous reduction. Teams that treat any related technical debt like financial debt, with a budget, interest and a repayment plan, gain far more control over their type quality in the long run than teams relying on one-off cleanup sprints.

Managing technical debt from any, the essentials at a glance

Measure, don't guess

type-coverage per module provides an objective baseline for every governance decision.

Budget, not recommendation

A CI gate that fails on a rising any count works more reliably than any request in review.

Ticket requirement

Every exception needs a ticket with a reason and target date, otherwise it effectively becomes permanent.

Ownership and reporting

Clear accountability per module and regular reports make reduction sustained instead of a one-off event.

11. FAQ: Managing Technical Debt from any

1Why isn't a one-time cleanup sprint enough against any-related technical debt?
Because without control over new exceptions the any count climbs back to the old level within a few months. Only a budget in the CI pipeline prevents permanent growth.
2How do you reliably measure any-related technical debt?
With tools like type-coverage instead of a simple grep, because grep also falsely counts comments and strings and does not provide a reliable baseline.
3What belongs in an any budget gate in CI?
A comparison of the current any count against a stored baseline that fails the build once the number rises, and automatically improves the baseline once it falls.
4Does every any usage need a ticket?
Yes, every new exception should document a reason, a target date and a responsible person, otherwise the context disappears within a few weeks.
5Who should be responsible for any-heavy modules?
A team or a person with a fixed time budget for reduction, similar to a CODEOWNERS assignment, so accountability does not stay diffusely spread across the whole team.
6How do you prioritize which any spots get fixed first?
By risk: any in frequently changed core functions deserves higher priority than any in rarely touched peripheral modules.
7How often should any-related technical debt be reported?
Monthly is enough in most teams, with total count, distribution by module and number of expired exceptions as fixed components.
8Is an any budget of zero realistic?
Rarely right away. A realistic goal is a steadily declining budget over several quarters, not an immediate ban without a transition period.
9What happens when a ticket passes its target date?
An automated reminder escalates the ticket into the active sprint backlog, so the exception does not silently become permanent.
10Is governance worth it in small teams too?
Yes, especially there a simple budget and ticket requirement prevent any usage from becoming an unnoticed habit before the team even realizes how large the balance has grown.