R08BREAKINGfunction

Symbol Unexported

Flags when a previously exported symbol loses its export keyword. All external modules importing this symbol will break.


Applies to

TypeScriptPythonGoJavaRust

Why it matters

When a symbol is unexported, every file in the codebase (and every downstream consumer of the package) that imports it will get a compilation error. Unlike parameter changes that affect call sites, unexporting affects import statements — a fundamentally different blast radius.

Example

Before (exported)
// utils.ts
export function calculateTax(amount: number): number {
  return amount * 0.2;
}
After (unexported)
// utils.ts (BREAKING — no longer exported)
function calculateTax(amount: number): number {
  return amount * 0.2;
}

What you see in the terminal

$ npx dg check

  [BREAKING] calculateTax (visibility_changed)
  src/utils.ts:2
  Symbol was unexported. All external importers will fail to resolve this symbol.

How detection works

The classifier checks the exported boolean field on both old and new signatures. If the old signature was exported and the new one is not, the rule fires.

if (oldSig.exported === true && newSig.exported !== true) {
  // BREAKING: symbol was unexported
}

Real-world scenario

A developer moves a utility function to be module-internal because it is only used within the file. However, three other modules import it. Those imports will break with "Module has no exported member 'calculateTax'".

Edge cases

  • In Go, changing an exported function from uppercase to lowercase (Calc -> calc) is equivalent
  • In Rust, removing the pub modifier triggers this rule
  • In Python, adding a leading underscore (_calc) is a convention, not enforcement