A product ships a "hide my activity" control. The web client reads the flag and stops rendering the activity row; the feed service still returns the activity in its response. Three weeks later a partner integration reading the same endpoint starts displaying it publicly. What happens next and which flaw is the cause?
Show the full answer Hide the answer
What happens next, in order
Minute one: the partner renders the activity. Nothing errors anywhere; every service is behaving exactly as specified.
Hour one: the partner's own cache and the partner's customers' screenshots now hold data the user believes is hidden. The copies are outside your control and they do not expire when you ship a fix.
Day one: you add the filter to the feed service. The user's activity stops flowing. The copies remain, so the remedy is no longer a code change - it is a notification to the partner, a purge request, and a count of how many downstream systems took the data in those three weeks.
Week one: someone asks how many other readers of that endpoint exist. The answer is usually eight to twelve: web, iOS, Android, the partner API, cached responses at the edge, the search index, the nightly export, and the feature pipeline that feeds recommendations. Enforcing the control in one of them delivered about one-eighth of a control while the product page said the control existed.
The mechanism
A user preference is only as strong as the lowest layer every reader must pass through. Put it above that layer and it is a rendering choice; put it at or below that layer - a row-level filter in the query path, or a predicate the data access layer applies unconditionally - and it holds for readers you have not written yet. That last clause is the whole value: the partner integration did not exist when the control shipped, and the correct enforcement point protects against readers that do not exist.
Why the other options fail
- Warehouse lag behind the nightly batch. A real and common defect, and it has a bounded cost: analysts see stale consent for under 24 hours inside a controlled system. It is worth fixing with a change-capture path, and it would not have exposed anything to a third party.
- Missing index on the consent table. A performance bug. It would show as latency on the feed read, not as a disclosure, and fixing it changes nothing about who can see the row.
- Users not told in the release notes. Low adoption of a control is a product problem. The users in this story did use the control; the harm is that it did not do what it said. A communications fix would have increased the number of people harmed.
The decision rule, and when client-side enforcement is not wrong
Enforce a preference at the lowest layer common to all readers; enforce in the client only when the preference is cosmetic. Theme, density, notification sound - fine in the client. Anything that decides whether a byte leaves the building belongs in the query path.
It flips when the data is public by definition. A seller's public listing on a marketplace has no hide semantics to enforce, and pushing a cosmetic preference into the query layer just makes every read conditional and every cache key longer for nothing.
What a strong answer adds
The remediation that the architecture should have made cheap: a readers register - a list of every system that reads this endpoint, with an owner - so that the question "who has a copy" has an answer in minutes rather than a week. Without it, the post-incident work is archaeology, and the user cannot be told what happened to their data.