An uncertain value is rarely just a value with a low confidence score. It has a history, an observation boundary and a deadline after which a consumer may need to interpret it differently.
A value is the end of a chain
Imagine an illustrative inventory service returning quantity 12. A consumer cannot tell whether that quantity was counted seconds ago, copied from yesterday's export, calculated after a reservation, or inferred from a message that arrived late. Each path may produce the same integer while supporting a different action. Displaying an approximate stock indicator and accepting a purchase require different assurances. If the response contains only the integer, the consumer must either trust too much or perform an expensive second investigation into how the value came to exist.
I think uncertainty should be represented where knowledge crosses a boundary. The goal is not to turn every API into a research database. It is to preserve the few dimensions that change permissible use: what was observed, when it was observed, which source supplied it, what transformation produced the current value, and which gaps remain. A producer may still return a definite integer. What it should avoid is implying that definiteness of representation establishes completeness, freshness or agreement with every other observer.
Keep kinds of uncertainty orthogonal
Missing, stale and conflicting are not mutually interchangeable labels. An observation can be both old and contradicted by another source. A missing observation can coexist with a fresh estimate derived from other inputs. Compressing these conditions into one status enum creates awkward precedence rules: does conflicting override stale, and does the losing label disappear? I prefer a contract with independent facts and a small set of explicit validity constraints. A consumer can then evaluate the combinations relevant to its own action.
That contract should also distinguish uncertainty about the world from uncertainty about computation. A sensor may genuinely fluctuate while the software reads it correctly. Alternatively, the world may be stable while the software lacks a recent sample. A parser failure is yet another issue. Retrying the network could help the second case and do nothing for the first. Increasing numerical precision would help neither. The useful classification is the one that suggests an effective next operation, rather than the one that looks tidy in a dashboard legend.
Freshness needs a reference point
HTTP caching makes freshness an explicit relationship between a lifetime and a calculated age. That is a useful reminder that a timestamp alone is not a freshness policy. An illustrative price observed at 10:00:00 and consumed at 10:00:07 is seven seconds old according to a shared clock, but whether seven seconds is acceptable depends on the use. A historical chart may welcome the observation. An execution decision may reject it. A producer can supply facts and suggested limits without pretending that every consumer has the same deadline.
Distributed clocks complicate the subtraction. If source and consumer clocks can differ by two seconds, an apparent age of seven seconds may represent an interval rather than a point. Receipt time supplies a local lower bound on how long the receiver has held the observation, but cannot establish when the source originally observed it. I would keep observed and received times separate, record the relevant clock assumptions, and avoid converting a negative apparent age into a magically fresh value. A future timestamp is a condition to investigate.
Boundary conditions should be specified too. If a consumer accepts observations younger than ten seconds, exactly ten seconds must have a defined outcome. Test that edge, a missing observation time and a clock correction. Small ambiguities in freshness rules become inconsistent behaviour when several services implement the policy independently.
References: [1] RFC 9111: HTTP Caching
Provenance should explain a transformation
The W3C provenance model distinguishes entities, the activities that use or generate them, and the agents responsible for those activities. An application rarely needs to expose that entire vocabulary in every response, but the distinction is productive. A source identifier tells me where an input came from; a derivation identifier tells me what changed it. Those are not equivalent. If an enrichment rule changes while the underlying observation stays the same, the result should be traceable to the rule version as well as the source.
The illustrative envelope below keeps an observation time, a receipt time, evidence identifiers and a derivation version. It represents missing coverage and conflicts without forcing them into a numeric probability. Evidence identifiers should resolve to a retained record or a documented redacted account, not a promise that every consumer can inspect private raw data. Provenance can preserve accountability without distributing sensitive input everywhere. The important property is that someone with the appropriate access can reconstruct why this particular value was produced at this particular boundary.
type Observation<T> = {
value: T;
observedAt: string;
receivedAt: string;
source: string;
evidenceIds: string[];
derivationVersion: string;
missingCoverage: string[];
conflicts: { evidenceId: string; description: string }[];
};References: [2] W3C PROV-DM: The PROV Data Model
Composition cannot manufacture freshness
Suppose an illustrative score combines three observations whose conservative ages are 1, 4 and 20 seconds. Recomputing the score now does not make all its evidence new. If every input is necessary, the consumer may need to consider the oldest required observation; if one input is optional, its age may affect only the part of the score it influences. The correct rule depends on the derivation. A single updatedAt field conceals that dependence and often lets an old fact masquerade as a recently verified conclusion.
A dependency graph also exposes invalidation. If a source retracts one observation, derived outputs should be identifiable without guessing which cache keys contain its influence. That does not require immediate recomputation of everything. It might require marking affected results as suspect until recomputation finishes. The operational choice should be explicit: serve the prior result with its status, stop serving it, or compute on demand. Silent retention is convenient until a downstream consumer interprets the unchanged number as evidence that nothing important happened.
Conflicts are data before they are policy
Two observations can disagree because they describe different times, units, identities or scopes. An illustrative inventory count of 12 and an available-to-sell quantity of 9 may both be correct if three units are reserved. A system that labels them conflicting before checking their meaning creates unnecessary escalation. Normalize the question first: quantity of what, at which location, under which reservation semantics and at what effective time? Only then does a numerical disagreement become a useful signal about the evidence.
When a genuine conflict remains, last-write-wins is a policy, not a proof. A later arrival may contain an older observation. A source with a faster retry loop may repeatedly override a more authoritative source. I would keep both records long enough to explain the choice and make the resolution rule versioned. Some applications need an authoritative source ordering; others need reconciliation or human review. There is no general merge function that turns disagreement into truth merely because the storage engine requires one final value.
Pay for context where it changes a decision
The counterargument is cost. Rich envelopes increase payload size, schema complexity, storage and the number of cases consumers must handle. That cost is real, and I would not attach a provenance graph to a static colour label. Start with a concrete incorrect action that the missing context could permit. If freshness changes whether a value is used, preserve freshness. If two sources can disagree in consequential ways, preserve source and conflict information. Avoid collecting metadata whose only consumer is a future investigation nobody can describe.
A useful acceptance test follows an observation through a delayed message, a duplicate delivery, a clock anomaly, a source correction and a derivation upgrade. Ask which facts survive each transition and which decision changes as a result. This is different from choosing whether to abstain: it designs the information on which that later choice depends. I want the receiving service to make its own policy decision while retaining the producer's epistemic limits. A compact honest envelope enables that; an unexplained scalar makes the receiver guess.
Sources and further reading
- RFC 9111: HTTP Caching
Specifies HTTP freshness and age calculations. The application-level observation envelope below is an analogy and an original proposal, not an implementation of HTTP caching.
- W3C PROV-DM: The PROV Data Model
Provides the entity, activity, agent and derivation vocabulary. The compact service contract and conflict policies here are my own design analysis.