Exported Mutable Variable

ID

javascript.exported_mutable_variable

Severity

high

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

medium

Resource

Code Smell

Language

JavaScript

Tags

code-smell, modules, state

Description

Reports an exported let or var binding that the module reassigns after declaring it.

Both spellings of the export are covered: the declaration form export let x = 0 and the clause form export {x} naming a module-level binding. A re-export (export {x} from './other') is not — the binding belongs to the other module.

A binding that is never reassigned is not reported, whatever it is declared with: the defect is the mutation, and javascript.prefer_const is what asks for const. Destructuring patterns are out of scope, since each sub-binding carries its own symbol and the remediation for a mixed pattern is a rewrite rather than a keyword.

Rationale

// Bad — session.js
export let currentUser = null;

export function signIn(user) {
  currentUser = user;
}
  • An ES module export is a live binding, not a copy. An importer that wrote import {currentUser} from './session' sees the new value the moment the module assigns it, at an instant the importer chose nothing about.

  • It reads like a constant at both ends: nothing at the import site says the name can change, and the assignment that changes it is a plain line in the middle of some function.

  • The two halves disagree in one common case. import {currentUser} tracks the binding, but destructuring the namespace (const {currentUser} = await import('./session')) copies it once, and a CommonJS consumer reading require('./session').currentUser gets whatever the value was at read time. The same module behaves differently depending on how it was imported.

Remediation

Export a function that reads the state, so every consumer goes through one door and the read is visibly a read.

// Good — session.js
let currentUser = null;

export function getCurrentUser() {
  return currentUser;
}

export function signIn(user) {
  currentUser = user;
}
// Also good — one exported object, mutated in place, so every import shares it
export const session = { currentUser: null };

export function signIn(user) {
  session.currentUser = user;
}

© 2026 Xygeni. All rights reserved