# ADR-002: No Serialisation Helpers — Callers Marshal/Unmarshal Themselves **Status:** Accepted **Date:** 2026-03-18 ## Context Cache and key-value store modules often provide convenience methods such as `SetJSON(ctx, key, value, ttl)` or `GetJSON(ctx, key, &target)` that handle JSON marshalling and unmarshalling internally. While convenient, this approach has drawbacks: - It encodes a single serialisation format (typically JSON) into the module's API, making it hard to use binary formats like protobuf or MessagePack for performance-sensitive paths. - It obscures marshalling errors, which can become hard to distinguish from network errors. - It requires the module to understand the caller's data types, coupling them together. - It adds dependencies (e.g. `encoding/json`) that are not needed for all callers. ## Decision The `valkey` module provides no serialisation helpers. It exposes only `Client() vk.Client`, and all marshal/unmarshal logic lives in the caller: ```go // caller marshals before writing b, err := json.Marshal(myValue) cmd := client.B().Set().Key(key).Value(string(b)).Ex(ttl).Build() client.Do(ctx, cmd) // caller unmarshals after reading result := client.Do(ctx, client.B().Get().Key(key).Build()) b, err := result.AsBytes() json.Unmarshal(b, &myValue) ``` This keeps the module at zero opinion on serialisation format, zero added dependencies beyond `valkey-go`, and zero abstraction cost. ## Consequences **Positive:** - Callers choose their own serialisation format with no module-level constraints. - The module has no encoding/decoding logic that needs testing or maintenance. - Binary formats, compressed payloads, and plain strings all work identically. **Negative:** - Every caller that stores structured data must implement its own marshal/unmarshal boilerplate, typically in a repository or cache layer. - There is no built-in protection against storing data with an incompatible format (e.g. writing JSON and reading with a protobuf decoder).