---
title: Avoid global Statement in Functions
impact: MEDIUM
impactDescription: The global statement creates hidden dependencies between functions and module-level state, making code order-dependent, untestable, and unsafe for concurrent use.
tags: python, global, state, quality, concurrency
---

## Avoid global Statement in Functions

Using `global var` inside a function creates an implicit dependency on module-level mutable state. The function's behavior changes depending on which other functions have already run, making it nearly impossible to test in isolation. In multi-threaded or async contexts, it introduces race conditions.

Pass state explicitly through parameters and return values, or encapsulate it in a class.

**Incorrect:**
```python
counter = 0
last_user = None

def process_request(user_id: int) -> None:
    global counter, last_user   # hidden mutation of module state
    counter += 1
    last_user = user_id
    do_work(user_id)

def get_stats() -> dict:
    return {"total": counter, "last": last_user}

# Tests are order-dependent and cannot run in parallel
def test_process_request():
    process_request(42)
    assert counter == 1   # depends on no other test having run first
```

**Correct:**
```python
from dataclasses import dataclass, field
from threading import Lock

@dataclass
class RequestTracker:
    _count: int = 0
    _last_user: int | None = None
    _lock: Lock = field(default_factory=Lock, compare=False)

    def record(self, user_id: int) -> None:
        with self._lock:
            self._count += 1
            self._last_user = user_id

    def stats(self) -> dict:
        return {"total": self._count, "last": self._last_user}

tracker = RequestTracker()

def process_request(user_id: int, tracker: RequestTracker) -> None:
    tracker.record(user_id)
    do_work(user_id)

# Tests are isolated
def test_process_request():
    t = RequestTracker()
    process_request(42, t)
    assert t.stats()["total"] == 1
```

**Acceptable module-level constants (no `global` needed):**
```python
# Constants never mutated — global statement not needed or used
MAX_RETRIES = 3
DEFAULT_TIMEOUT = 30.0
```

**Tools:** Ruff `PLW0603` (global-statement), Pylint `W0603`, SonarQube Python `S1854`, flake8
