Code Review · Python

Python Code Review: Mutable Shared State

Review a Python class that accidentally shares a mutable list across all instances.

Code ReviewPythonClassesBugsState

Code Review Exercise

Review the following Python class. Assume it is used to collect validation errors while processing different requests.

python
class ValidationResult:
    errors = []

    def add_error(self, message):
        self.errors.append(message)

    def is_valid(self):
        return len(self.errors) == 0


first = ValidationResult()
first.add_error("Email is required")

second = ValidationResult()
print(second.is_valid())

What issues do you see before scrolling further?

Review Findings

Issue #1: errors is shared across all instances

errors is defined on the class, not inside __init__. Because the list is mutable, every ValidationResult object shares the same list.

Issue #2: One request can leak state into another request

In the example, first adds an error. Then second may appear invalid even though no error was added to second. That is a serious correctness bug in request-processing code.

Issue #3: The code looks object-oriented but behaves globally

A reader may assume each instance has its own validation result. Instead, the class-level list behaves like shared global state. This makes the behavior surprising and hard to debug.

Issue #4: The bug gets worse in tests and long-running services

Shared mutable state can cause flaky tests because one test may leave data behind for another test. In a web service, one request can accidentally affect a later request.

What Actually Prints?

Many people expect this to print True becausesecond is a new object.

python
print(second.is_valid())  # False

It prints False because first.errors andsecond.errors refer to the same class-level list.

Fixed Version

Move the mutable list into __init__ so every instance receives its own list.

python
class ValidationResult:
    def __init__(self):
        self.errors = []

    def add_error(self, message):
        self.errors.append(message)

    def is_valid(self):
        return len(self.errors) == 0


first = ValidationResult()
first.add_error("Email is required")

second = ValidationResult()
print(second.is_valid())  # True

Even Better: Add Type Hints

Type hints make the expected state clearer and help readers quickly understand what the class stores.

python
class ValidationResult:
    def __init__(self) -> None:
        self.errors: list[str] = []

    def add_error(self, message: str) -> None:
        self.errors.append(message)

    def is_valid(self) -> bool:
        return len(self.errors) == 0

When Are Class Variables Okay?

Class variables are not always wrong. They are useful for constants or intentional shared configuration.

python
class ValidationResult:
    MAX_ERRORS = 100  # OK: constant shared by all instances

    def __init__(self) -> None:
        self.errors: list[str] = []

The review question is not "Are class variables bad?" The better question is: "Is this state supposed to be shared?"

What Interviewers Expect

  • Catch that errors = [] is shared across instances.
  • Explain why mutable class variables are dangerous when the state is supposed to belong to each object.
  • Suggest moving instance-specific state into __init__.
  • Mention test flakiness or request-to-request leakage as practical production concerns.

Interview Follow-Ups

Is every class variable bad in Python?

No. Class variables are fine for constants or intentionally shared values. The problem is mutable state that should have belonged to each instance.

How is this different from a mutable default argument?

Both bugs involve shared mutable state. Mutable default arguments share state across function calls. Mutable class variables share state across instances.

Why can this be hard to catch in tests?

Tests may pass or fail depending on execution order. If one test mutates shared state and another test runs later, the second test can start with unexpected data.

What is the simplest fix?

Put instance-specific mutable fields inside __init__, usually with self.errors = [].