Encoding constraints in API signatures
Nicolae Vartolomei · 2026/03
“Hide the limit and find it in the postmortem.”
A function materializes a scattered buffer into a contiguous string. Without a size limit, a large buffer means a large allocation. A limit is necessary. The question is where it lives.
Three designs, each communicating something different:
// Version A
std::string LinearizeToString() const;
// Version B
std::string LinearizeToString(size_t max) const;
// Version C
std::string LinearizeToString(LinearizationLimit max) const;Version A hides the limit in its implementation. The caller sees a clean, parameterless function and concludes there is nothing to think about. A hypothetical 128 KiB ceiling is invisible at the call site, invisible in review. A future caller processing data from the network hits it in production.
Version B surfaces the limit. Every call site states an expectation: this data
is at most N bytes. But size_t accepts any runtime value, computed, read
from config, passed through layers of indirection. The decision can be
deferred.
C++20 gives us a way to close that gap. Version C:
constexpr size_t kMaxLinearizationLimit = 128 * 1024;
class LinearizationLimit {
public:
consteval LinearizationLimit(size_t v) : v_(v) {
if (v > kMaxLinearizationLimit) {
throw std::invalid_argument("limit exceeds 128 KiB");
}
}
size_t value() const { return v_; }
private:
size_t v_;
};constexpr size_t kMaxMetadata = 32 * 1024;
LinearizeToString(kMaxMetadata); // ok
constexpr size_t kMaxPayload = 1024 * 1024;
LinearizeToString(kMaxPayload); // compile error: limit exceeds 128 KiB
size_t limit = GetBufferSize();
LinearizeToString(limit); // compile error: not a constant expressionThe consteval constructor must evaluate at compile time. The limit must be a
deliberate, upfront decision rather than something computed or deferred.
If every caller passes the same value, the parameter looks like ceremony. But the parameter isn’t for today’s callers who know the context. It’s for tomorrow’s caller who doesn’t and would otherwise discover the constraint in a postmortem rather than in a signature.
Comments describe contracts. Signatures encode them. consteval enforces them,
at compile time.