Types are a basic tool of software design (2018)
Types as a central tool for software design strongly divide programmers: some argue that explicitly modeling data and function signatures, whether in Haskell, TypeScript, F#, Ada or Python’s type hints, improves documentation, refactoring, and early error detection, especially in large or long-lived codebases. Others counter that overemphasizing types downplays architecture, algorithms, performance and IO concerns, adds maintenance overhead, and that dynamically typed systems can achieve robustness through tests, contracts, and disciplined design. The exchange highlights a broader question of how much correctness and design intent should be enforced by compilers and tooling versus left to developer judgment and runtime checks.
Scope of “Types as the Basic Tool”
- Some agree that once function bodies are removed, what remains (types, signatures, data structures) is essentially the design.
- Others argue this is overstated: design also includes algorithms, IO, performance, distribution, and UX, not just types.
- Several see types more as “vocabulary” or “contracts” than “tools,” and find the title rhetorically inflated.
Static vs Dynamic Typing in Design
- Many report starting design from data structures, function signatures, and types, even before implementation.
- Others say they think first about architecture and non-functional requirements (location of data, policies, latency), with types following later.
- A recurring theme: even in dynamic languages, developers must implicitly think about types; writing them down just offloads checking to tools.
TypeScript, Tooling, and the Turbo PR
- One camp sees TypeScript annotations as lightweight documentation, refactoring aid, and “always-on unit tests,” especially valuable in large JS codebases.
- Another welcomes removal of TypeScript (and tools like Prettier) as simplifying the code; they prefer the “after” state, even at the cost of compiler checks.
- Disagreement over whether losing explicit optionality and structure (e.g., an optional parameter) is adequately compensated by reading implementation bodies.
- Side debate on trailing commas: some want them for clean diffs; others see this as better solved by smarter diff tools.
Type Inference and Ripple Effects
- F#, Scala, Haskell, and Rust are discussed: inference can make code concise but changes can “ripple” and produce confusing error chains.
- Some mitigate this by adding explicit types as “anchors” or using interface files.
- Viewpoint: these ripples exist in any system; static types just surface them earlier.
Domain Modeling, DDD, and Contracts
- Several connect the article’s thesis to longstanding ideas: “show me your data structures and I’ll understand your code.”
- Domain-Driven Design is cited as a parallel: model the domain via types and the rest of the system follows.
- Many like thinking of types as contracts or interfaces: changing behavior often forces a signature change, surfacing broken “contracts” at compile time.
Dynamic Languages and Large Codebases
- Some report painful experiences maintaining large untyped Ruby/Python/JS projects: unclear interfaces, harder refactors, more runtime or production bugs.
- Others claim success with big JS codebases without static types, relying on careful design, runtime checks, and tests.
- There’s disagreement over whether lack of types mainly hurts poorly written code or becomes a systemic liability at scale.
Alternative Perspectives
- A few suggest the real “basic tools” are actors, messages, or denotational semantics rather than functions/types.
- One angle reframes “types” as just one form of “templates” or patterns used in all design, from games to software.