Static vs Dynamic Typing
Compile-time vs runtime type checking
Static and dynamic typing describe when a language checks types. Statically typed languages verify types at compile time before the code runs, catching type errors early. Dynamically typed languages check types at runtime, offering more flexibility but discovering type errors only when the code executes.
Static Typing
In a statically typed language, every variable, parameter, and return value has a type that is known and verified at compile time. The compiler checks that operations are valid for the given types before the program runs — you can't accidentally pass a string to a function expecting an integer. Languages like Java, C, C++, Go, Rust, TypeScript, and Kotlin are statically typed. Static typing catches entire categories of bugs before the code ever executes, enables powerful IDE features (autocompletion, refactoring, go-to-definition), and produces self-documenting code where function signatures specify exactly what goes in and comes out. The trade-off is more upfront type declarations and less flexibility.
Dynamic Typing
In a dynamically typed language, variables don't have fixed types — types are associated with values at runtime, not variables at compile time. A variable can hold a string one moment and a number the next. Type errors are only discovered when the offending code actually executes. Languages like Python, JavaScript, Ruby, PHP, and Lua are dynamically typed. Dynamic typing enables rapid prototyping, concise code, and flexible patterns like duck typing ('if it quacks like a duck, treat it as a duck'). The trade-off is that type-related bugs can lurk undetected until specific code paths run in production, which is why dynamically typed languages benefit greatly from comprehensive test suites.
Key Differences
- **When types are checked**: Static typing checks at compile time. Dynamic typing checks at runtime. - **Variable types**: Static: variables have declared types. Dynamic: variables can hold any type at any time. - **Error detection**: Static typing catches type errors before running. Dynamic typing catches them only when the code executes. - **Verbosity**: Static typing requires type annotations (mitigated by type inference). Dynamic typing is more concise. - **IDE support**: Static typing enables superior autocompletion, refactoring, and error detection. Dynamic typing makes IDE tooling harder. - **Flexibility**: Dynamic typing allows duck typing and flexible patterns. Static typing is more restrictive but more predictable.
When to Use Each
**Use static typing** for large codebases, long-lived projects, team collaboration, performance-critical systems, and anything where catching bugs early saves significant cost (backend services, financial systems, embedded software). TypeScript's popularity shows that even dynamic-language ecosystems crave static typing at scale. **Use dynamic typing** for prototyping, scripting, small utilities, data analysis, and domains where flexibility and rapid iteration matter more than type safety. Python and JavaScript dominate these spaces for good reasons.
Analogy
**Static typing** is like a postal system that checks every letter's address format before accepting it — wrong format gets rejected immediately. You spend a bit more time at the counter, but your mail never gets lost due to a bad address. **Dynamic typing** is like a postal system that accepts any letter and tries to deliver it. If the address is good, it arrives fast. If the address is wrong, you find out days later when it comes back undeliverable (a runtime error). Quick to send, but failures are discovered late.