Stringly Typed Code
Stringly typed code models structured or constrained data as plain strings, losing compiler help and scattering parsing logic. Introduce enums and validating value objects, parsing untyped input into real types at the boundary.
Stringly typed code (a pun on "strongly typed") uses plain strings to model data that actually has structure or a constrained domain. A status field holds "active", "ACTIVE", or "Activated"; money is passed as "$10.50"; a date arrives as "2026-06-27" and is re-parsed in five places. The string becomes a universal but meaningless container.
Why It Happens
Strings are frictionless. They serialize trivially, cross every boundary (HTTP, JSON, env vars, CLI), and require no class definition. When data first enters as text, it is tempting to keep it as text all the way through rather than parse it into a proper type at the edge. Teams under time pressure default to Map<String,String> or string constants instead of modeling the domain.
Why It Hurts
The compiler can no longer help. A typo like "acitve" compiles and ships, failing only at runtime — or worse, silently. Comparisons must worry about case and whitespace. Valid values are not enumerable, so IDEs cannot autocomplete and refactoring tools cannot find usages. Business rules get re-implemented as ad-hoc string parsing scattered across the codebase, and invariants (a currency code is exactly three letters; an order status is one of four values) are never enforced in one place.
Warning Signs
- Status, type, or category fields typed as
String. - The same string parsed or split in many locations.
- Equality checks like
s.equalsIgnoreCase("yes")sprinkled around. - String constants used where an enum would fit.
Better Alternatives
Introduce enumerated types for fixed value sets so the compiler enforces the domain. Wrap structured strings in small value objects (EmailAddress, Money, CurrencyCode) that validate on construction and parse once. Apply domain-driven design to give meaningful concepts real types. Parse, don't validate: convert untyped input into typed values at the system boundary, then work with typed values internally.
How to Refactor Out of It
Pick one stringly typed concept with clear rules. Create a type for it (enum or value object) with a single parse/factory method that validates. Replace the string at the boundary first, then push the new type inward, letting the compiler flag every site that needs updating. Remove duplicated parsing as you go. Add tests for invalid inputs to confirm validation now lives in one place. Repeat per concept; do not try to convert everything at once.