Use Your Type System

Types as Documentation, Validation, and “First-Line Tests”

  • Many commenters endorse using richer types to document data models, aid refactoring, and “make bad state unrepresentable.”
  • Domain-specific types (IBAN, AccountId, UserId, Money, Percentage, TempC/TempF, absolute vs delta time/temperature) allow validation at construction and encode invariants once.
  • Some describe a workflow akin to TDD but with the type checker: tighten types until a bug causes compile-time failure, then fix.

Newtypes, Value Objects, and Primitive Obsession

  • The core pattern is widely recognized: newtypes / strongly typed identifiers / value objects / painted types / “safety through incompatibility.”
  • It’s contrasted with “primitive obsession” / “stringly typed” code where everything is string or int.
  • Examples span many languages: Go named types, Rust newtypes, C# marker-generic Id, TS branded and template-literal types, Python NewType, Nim/Ada/Pascal subranges, F# units-of-measure, etc.

Range, Refinement, and Dependent Types

  • Several want bound integers and refinement types (“int in [0,10)”, array indices guaranteed in range, HTTP-status-to-response typing).
  • Ada, Pascal, Nim, ATS, TypeScript tricks, F# measures, Idris, Liquid Haskell, and Wuffs are cited as partial realizations.
  • Others warn of undecidability, type-level “puzzles,” recursion limits, and gnarly types that hurt usability.

OOP, Constructors, and Correct-by-Construction

  • “Correct by construction” is emphasized: enforce invariants via constructors or factory functions; make it hard to create invalid UUIDs/IDs.
  • Debate over exceptions vs Result/Either: checked exceptions viewed by some as powerful type-level contracts, by others as ergonomically “contagious” and noisy.

Costs, Over-Typing, and Dynamic Alternatives

  • Skeptics report painful codebases where “everything is a unique type,” leading to boilerplate conversions, awkward interop, and performance worries (heap allocation, excessive conversions).
  • Some argue that in many domains good tests, schemas, and dynamic validation (e.g., Clojure Spec/Malli) are more flexible and less brittle, especially for startups.
  • Consensus: strong, domain-aware types are extremely valuable, but can be overdone; types, tests, and runtime checks are complementary tools, not substitutes.