When designing decoders for your application, follow these architectural principles:
1. Name decoders after Data Types, not Fields
Avoid naming a decoder after the property it validates (e.g., labelsDecoder). Instead, name it after the shape of the data it describes (e.g., commaSeparated). This makes the decoder reusable across different parts of your schema.
2. Keep edge cases outside decoders
To maintain composability, keep decoders focused on a single responsibility. Instead of building a decoder that handles both a specific format AND null values, build a decoder for the format and wrap it in nullable() or nullish() at the call site (inside your object() definition).
Bad (Low Reusability):
const messyString = string.transform(s => s.trim()).nullable(); (Harder to reuse the trim logic elsewhere)
Good (High Reusability):
const trimmedString = string.transform(s => s.trim());
const field = nullable(trimmedString);