Static vs Dynamic Typing

6 min read

Reading Progress0%
CS Fundamentals Index
Tier 1 -- Foundations
Tier 2 -- Core Concepts
Tier 3 -- Debugging & Tradeoffs
CS Fundamentals Index
Tier 1 -- Foundations
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.

QUICK CHECK

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?

Choose one answer

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.

QUICK CHECK

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?

Choose one answer

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 accept f("hello") and may crash or return nonsense only when the body does something int-specific. Run mypy/pyright if you want enforcement.
  • auto (C++) and var (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.
QUICK CHECK

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?

Choose one answer

4. Language Differences

AspectPythonJavaC++
When types are checkedRuntimeCompile timeCompile time
Must declare variable types?No (hints optional)Yes (or var)Yes (or auto)
Type errors caught atExecution of the offending lineCompilationCompilation
Generic programmingDuck typingGenerics (erased)Templates (monomorphized)
Runtime cost of typesHigher — every operation does a type checkNear zero — types are checked once, machine code is specializedNear zero — templates produce specialized machine code
Refactoring safetyWeak — rename a method, find out in productionStrong — compiler flags every affected call siteStrong — 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.

QUICK CHECK

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?

Choose one answer

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 + b does 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 auto liberally. 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.

QUICK CHECK

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?

Choose one answer

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 var in Java and dynamic typing?"var is 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.