← Back to Python

Monkey Patching

PythonMidpython

The Question

What is monkey patching and what are the risks?

What a Strong Answer Covers

  • modify at runtime
  • "testing use
  • "production risk

Senior-Level Answer

Monkey patching is the practice of dynamically modifying or replacing attributes, methods, or modules at runtime — without altering the original source code. In Python, because everything is an object and objects are mutable, this is syntactically trivial: you assign a new function to an attribute of a class or module, and all subsequent calls to that attribute invoke your replacement.

The most common legitimate use is **test mocking**. Rather than making a real HTTP request in a unit test, you replace `requests.get` with a function that returns a fixed response: `requests.get = lambda url, **kw: MockResponse(200, data)`. The `unittest.mock.patch` decorator and context manager automate this pattern and restore the original after the test block exits, preventing test pollution.

Another use is **hotfixing third-party libraries** when you cannot wait for an upstream release — you override a buggy method on an imported class at application startup. This is risky and should be temporary.

The risks are substantial. **Hidden coupling**: code that reads the patched attribute has no knowledge of the substitution. If the patch is applied globally (not scoped to a test), all code in the process is affected. **Ordering dependency**: the patch must be applied before any code imports and caches a reference to the original; `from module import func` captures the original reference, bypassing a later `module.func = new_func` patch — a common source of confusing test failures. **Debugging difficulty**: a patched attribute does not appear in the class definition, making tracing broken behavior hard. **Fragility**: if the library being patched is updated and changes the patched method's signature, the patch silently continues with a mismatched interface.

In testing, `unittest.mock.patch` is the correct tool — it restores originals automatically, scopes the patch properly, and provides assertion capabilities. For production code, dependency injection is almost always a better alternative: pass the dependency as an argument rather than patching it globally. This makes the dependency explicit, testable, and auditable in code review.

What Separates a 2/3 from a 3/3

2/3 — Passing but Incomplete

Correctly defines monkey patching with an example and names testing as the primary use case. Identifies hidden coupling and debugging difficulty as risks.

3/3 — Strong Answer

Explains the `from module import func` gotcha (cached reference), discusses scoping via unittest.mock.patch vs. global patching, contrasts with dependency injection as the preferred production alternative, and can articulate why monkey patching violates the principle of least surprise.

Common Mistakes

  • Not knowing the 'from module import func' gotcha — this trips up even experienced Python developers when writing tests.
  • Treating monkey patching as equivalent to dependency injection — they solve similar problems but with very different coupling and auditability properties.
  • Overlooking that patches applied without unittest.mock.patch persist across tests, causing order-dependent test failures.
  • Presenting monkey patching as a standard production pattern rather than a last resort.

Follow-Up Questions

  • Why does 'from module import func' break a monkey patch applied to module.func? — from...import copies the reference at import time. The local name points directly to the original object. Replacing module.func later changes the module attribute but not the already-bound local reference.
  • How does unittest.mock.patch determine where to patch — the definition location or the usage location? — Patch where the name is looked up (the usage location), not where it is defined. If code does 'from mymodule import os', patch 'mymodule.os', not 'os.path.join' directly.
  • What is dependency injection and why is it a better alternative to monkey patching in production code? — DI passes dependencies as constructor/function arguments, making them explicit, replaceable via interfaces, and auditable. Monkey patching is implicit and global — DI is explicit and local.
  • Are there cases where monkey patching is the right answer even in production code? — Gevent and eventlet use monkey patching to replace blocking stdlib calls with cooperative I/O at startup — a controlled, well-documented, one-time global patch that is the entire point of the library.

Related Questions

  • GIL — What It Is and What It Protects
  • ThreadPoolExecutor & ProcessPoolExecutor
  • Decorators — Under the Hood
  • Generators & yield
  • Context Managers

Can You Explain This Cold?

Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.

Get Graded — Free Assessment
GrindQuestionsAITechnical interview assessment
TermsPrivacyAbout