6 min read
CS Fundamentals Index
Tier 1 -- Foundations
Object-Oriented Programming
Tier 2 -- Core Concepts
Tier 3 -- Debugging & Tradeoffs
CS Fundamentals Index
Tier 1 -- Foundations
Object-Oriented Programming
Tier 2 -- Core Concepts
Tier 3 -- Debugging & Tradeoffs
Static vs Dynamic Typing
1. What Is It?
A type system answers: "What kind of value is this, and what operations are legal on it?" The split between static and is about when that question gets answered. answers at compile time — before the program ever runs. answers at runtime — the moment the operation actually executes.
Without a clear picture of this: you get confused about why some bugs surface instantly in Java or C++ but only appear in production for Python, why refactoring a large Python codebase is terrifying, and why a Java method signature is so much noisier than a Python function definition. The typing discipline shapes the tooling, the error messages, the performance, and the feel of the language.
A team ships a Python web service to production and discovers a bug where a string is passed to a function expecting a number — causing a crash only when a specific API endpoint is hit by real users. A colleague says this would have been caught before deployment in a statically typed language like Java. What is the core reason this is true?
2. How It Works
(Java, C++): every , parameter, return value, and expression has a type known to the compiler. The compiler checks that every operation is legal against those types. If x is an int and you try x.append(5), the compiler refuses — the program never runs.
(Python): names don't have types; values have types. The compiler (interpreter) doesn't check in advance whether an operation makes sense. It attempts the operation at runtime and raises an error (typically TypeError or AttributeError) if the value doesn't support it.
# Python — dynamic def add(a, b): return a + b add(1, 2) # 3 add("x", "y") # "xy" add(1, "y") # TypeError at RUNTIME: unsupported operand type(s)
// Java — static int add(int a, int b) { return a + b; } add(1, 2); // 3 add(1, "y"); // COMPILE ERROR: incompatible types
// C++ — static int add(int a, int b) { return a + b; } add(1, 2); // 3 add(1, "y"); // COMPILE ERROR
Strong vs. weak is a different axis
Strong typing means the language refuses to silently coerce incompatible types. Weak typing means it will. Python is dynamically and strongly typed — it will refuse 1 + "y" at runtime rather than guess. JavaScript is dynamically and weakly typed — 1 + "y" becomes "1y". Don't conflate the two axes.
Gradual typing
Modern Python has type hints (def add(a: int, b: int) -> int:). These are not enforced at runtime by the interpreter. They are enforced by external checkers (mypy, pyright) — effectively bolting a static checker onto a dynamic language. The runtime behavior is unchanged.
A Python backend service has a function def process(data, handler) that is called in hundreds of places. A developer passes an integer where handler expects an object with a .run() method. When will this error be caught?
3. What You Actually Need to Know
- Where errors surface tells you the typing discipline. Compile-time error → static. Runtime
TypeError→ dynamic. - Type annotations in Python are not runtime checks.
def f(x: int)will happily acceptf("hello")and may crash or return nonsense only when the body does something int-specific. Run mypy/pyright if you want enforcement. auto(C++) andvar(Java 10+) are still static. The compiler infers the type — it's not dynamic. The type is fixed after inference.- Duck typing is a common dynamic idiom: "if it walks like a duck and quacks like a duck, it's a duck." Any with the right method works, regardless of . Static equivalents are interfaces (Java) and concepts / templates (C++).
- Debugging clues:
- Compile fails with "incompatible types" / "cannot convert" → static language doing its job.
- Runtime
TypeError/AttributeError: 'NoneType' object has no attribute 'X'→ dynamic failure; the value at runtime wasn't what you assumed. - Python code "works" in tests but fails in prod on an untested code path → classic dynamic-typing hazard; a whole branch was never type-checked because it was never executed.
A Python web service passes all unit tests in CI, but intermittently crashes in production with a TypeError on a specific endpoint that handles an edge-case input. The function has type annotations, but no static analysis tool was run. What is the most likely explanation for this behavior?
4. Language Differences
| Aspect | Python | Java | C++ |
|---|---|---|---|
| When types are checked | Runtime | Compile time | Compile time |
| Must declare variable types? | No (hints optional) | Yes (or var) | Yes (or auto) |
| Type errors caught at | Execution of the offending line | Compilation | Compilation |
| Generic programming | Duck typing | Generics (erased) | Templates (monomorphized) |
| Runtime cost of types | Higher — every operation does a type check | Near zero — types are checked once, machine code is specialized | Near zero — templates produce specialized machine code |
| Refactoring safety | Weak — rename a method, find out in production | Strong — compiler flags every affected call site | Strong — same as Java |
Python's dynamism is why it's expressive and why large Python codebases need rigorous testing and type hints to stay maintainable. Java/C++'s static discipline is why they're verbose and why the compiler can catch whole categories of bugs before you run anything.
Your team is maintaining a large Python backend service. A developer renames a core method used across 50 different modules. What is the most likely outcome compared to making the same rename in a Java codebase?
5. Tradeoffs & Decisions
- Development speed vs. refactoring safety. lets you prototype fast — no ceremony, no casts. pays off the moment the codebase is large or long-lived — the compiler finds bugs that would otherwise lurk.
- Expressiveness vs. performance. Dynamic dispatch means every
a + bdoes a type check. Static types let the compiler generate straight-line machine code. For tight loops and systems code, this matters a lot. - Strictness vs. flexibility for APIs. A statically typed function declares exactly what it accepts. A dynamically typed function accepts anything that satisfies the operations inside — more flexible but harder to document and reason about.
- Modern reality. Most large Python codebases adopt type hints + mypy. Most modern C++ uses
autoliberally. The lines are blurring, but the underlying model — when checks happen — still determines behavior.
If you see a runtime TypeError in Python, it usually means a code path was never exercised. You'd choose when the cost of that surprise (in user-facing failures, refactor fear, or perf) outweighs the cost of upfront type declarations.
A startup is building a quick proof-of-concept backend API to demo to investors next week. The team expects the codebase to be thrown away or heavily rewritten afterward. Which typing approach offers the most relevant advantage for this specific situation, and why?
6. Interview Cheat Sheet
- Static = compiler checks types before running. Dynamic = interpreter checks types as operations execute.
- Strong vs. weak is orthogonal. Python is dynamic + strong. JavaScript is dynamic + weak. Java and C++ are static + strong.
- catches whole classes of bugs (typos, wrong argument types, missing methods) before the program runs; catches them only when that line of code executes.
- Type hints in Python are static analysis on top of a dynamic language — not runtime enforcement unless you wire up a validator.
- generally enables better performance (monomorphized code, fewer runtime checks) and safer refactoring; enables faster prototyping and more flexible duck-typed APIs.
Follow-ups:
- "Is Python type-safe?" — It's dynamically and strongly typed. It won't silently coerce
1 + "y", but it will let you pass the wrong type to a function and only fail when the type is actually used. - "What's the difference between
varin Java and dynamic typing?" —varis inferred at compile time and the type is then fixed. Dynamic typing means the same name can hold any type at any time. - "Why would you add type hints to Python?" — Earlier detection via mypy/pyright, better editor autocomplete, clearer API contracts in docs — without changing runtime behavior.
Glossary History
Click dotted jargon to save explanations here.
Glossary History
Click dotted jargon to save explanations here.