Skip to content

python.global_mutable_state

Correctness High

Detects module-level mutable variables (lists, dicts, sets) not marked as Final or using constant naming convention.

Global mutable state causes problems that only show up in production:

  • Race conditions — Multiple threads modify the same data concurrently
  • Request leakage — In web apps, data from one request leaks into another
  • Test pollution — Tests affect each other through shared state
  • Debugging nightmare — State changes from anywhere, hard to trace

Single-threaded development hides these bugs. Production with concurrent workers reveals them.

# ❌ Before
cache = {} # Module-level mutable dict
def get_user(user_id):
if user_id not in cache:
cache[user_id] = fetch_user(user_id)
return cache[user_id]

In a multi-worker web server, this cache is duplicated per process and has no thread safety.

# ✅ After
from threading import Lock
class UserCache:
def __init__(self):
self._cache = {}
self._lock = Lock()
def get(self, user_id):
with self._lock:
if user_id not in self._cache:
self._cache[user_id] = fetch_user(user_id)
return self._cache[user_id]
# Or use a proper caching library
from functools import lru_cache
@lru_cache(maxsize=1000)
def get_user(user_id):
return fetch_user(user_id)
  • Module-level {}, [], set() assignments
  • Module-level variables that get mutated elsewhere
  • Excludes CONSTANT_CASE names (assumed intentional)
  • Excludes Final annotated variables

Unfault suggests encapsulating mutable state in classes with proper synchronization.