- Section
- languages-frameworks
- Kind
- Editorial
- Reading time
- 3 min
- Updated
- 8 September 2026
Gradual Typing For Existing Codebases: Add Types Without Widespread Rewrites
Teams adopting static typing can gradually tighten a language’s type system from the boundaries inward, without rewriting the entire code base. The goal is not to end up fully typed, but to make the pull of static safety ever incrementally stronger.
Pyre Checker and mypy for Python both assume gradual adoption of an untyped base with new code getting the strictness. The Python Enhancement Proposal (PEP) 484 defines Python's optional static typing model, adopted in Python 3.5 (2015). PEP 484 provides the ability for incompatible type checkers. Python's typing ecosystem includes multiple checkers, all using the same model defined in PEP 484.
Pyre Checker was published in 2017. According to their documentation, it is "designed from the ground up to support gradual typing," meaning that type hints can be added slowly, and dynamic typing remains an option. Python’s own typing ecosystem includes multiple checkers, with PEP 484 being the shared model they implement.
For JavaScript, TypeScript applies the same idea of gradual typing and incremental migration. The TypeScript Handbook explains a conversion path for JavaScript to TypeScript that starts with JavaScript code, then purely TypeScript, and then TypeScript in strict mode. It indicates using // @ts-check in a JavaScript file to apply gradual typing in JavaScript files. It recommends starting with allowJs: true, then checkJs, renaming files, and tightening strict flag one at a time. That guide also recommends migrating outwards from leaf modules up the tree as dependency chains are fleshed in.
Python and JavaScript have a long history of accumulation from untyped, but PHP is explicitly building the capability. PHP added typed properties in 7.4, union types in 8.0, and readonly properties in 8.1.
That is not to say the transition will be easy. End-to-end rewrites to add strong typing are rare in deep codebases. The biggest interruptions come from starting at internal dependencies and working out, not from cleansing the codebase with a single pass often. Guardrails like mypy’s PEP 484-compliant Any type in Python, or TypeScript’s operational mode for JavaScript, let today’s core logic remain static while new work gets safer.
This smooth model exists no matter the flavor of typing you choose. Yet, So, incremental typing offers more than a project manager’s peace of mind. Code that knows what types of value it consumes and produces is easier to orchestrate. Programmers working in deeper integrations have better imagination and assurance about the envelope of a component. Pinning a type to a regular database schema minimizes shape shifts in the database. Marking an API boundary lets proc generators catch up. Marking a message format lets debuggers recognize errors.
If a warning's a false positive inside the chain, passed over with two warnings, then let the team know why. They can choose to remember or exclude warnings appropriately, as per the company standard See best practices>warning_false positives config.
In conclusion, the value of gradual typing for existing codebases lies in its ability to progressively improve code quality and reliability without disrupting ongoing work. By adding type annotations gradually, teams can leverage the benefits of static typing, such as improved code comprehensibility, reduced runtime errors, and enhanced tooling support, while still maintaining flexibility and productivity. This approach allows for a smooth transition from dynamic to static typing, enabling teams to reap the advantages of modern typing systems without the need for a complete rewrite of their existing codebase.