Every follower tracker answers its questions with set operations, whether it admits it or not. When we built the historical comparison in Follower Change, the engineering turned out to be the easy part — a few set differences over normalized username lists. The hard part was epistemology: making sure the words in the UI never claimed more than the sets could prove. This post is the data model, and the honesty rules that fall out of it.
Two lists, three questions
A platform data export gives you, for one moment in time, two sets:
- F — your followers
- G — accounts you follow ("following")
From a single export, exactly one comparison is sound:
- G \ F — accounts you follow that don't follow you back. This is a statement about now.
Nothing in a single snapshot describes change. Change requires two exports of the same list at different times — say F₁ (earlier) and F₂ (later):
- F₂ \ F₁ — accounts present later that weren't there before: new followers (or returners).
- F₁ \ F₂ — accounts present before that are absent now.
That second set is where the lying starts.
Absence is not an action
F₁ \ F₂ is routinely labeled "unfollowers." But set membership can't tell you why an element disappeared, and the possible causes are not interchangeable:
- the person unfollowed you;
- the account was deleted — by its owner, or by the platform;
- the account was suspended;
- the account was renamed, and the same person is sitting in F₂ under a new handle, possibly also appearing in F₂ \ F₁ as a phantom "new follower";
- the export itself was partial — a split or truncated file can manufacture absences wholesale.
So the honest label for F₁ \ F₂ is "may have unfollowed" — or, in product language, "lost followers." Anything stronger ("X unfollowed you on Tuesday") is a claim the data cannot support, because exports are snapshots: they carry no event log and no timestamps for individual joins or leaves. Any tool that gives you an exact unfollow date from export data is interpolating — or inventing.
The rename case deserves emphasis because it corrupts both difference sets simultaneously: one departure and one arrival, both phantoms, for a single human who changed nothing about following you. Username-keyed diffing cannot detect it without stable account IDs, and most exports don't provide them consistently. A well-behaved tool says this out loud in its limits section instead of letting the numbers pose as certainty.
Normalization decides your error rate
Before any diffing, both snapshots pass through the same normalization, because exports are inconsistent in ways that masquerade as change:
- Case: handles are case-insensitive on most platforms but exported inconsistently; lowercase everything.
-
The
@prefix: present in some formats, absent in others, occasionally present within a file. Strip it as a leading character only — an@anywhere else is data. - Whitespace and empties: blank entries (trailing commas, empty JSON strings) are excluded, or your "new followers" list fills with ghosts.
-
Split files: some platforms shard large lists (
followers_1.json,followers_2.json, …). Compare against the union of the shards — diffing against one shard manufactures thousands of fake departures.
Get normalization wrong and the set logic is flawless arithmetic over garbage.
A worked miniature
Earlier export F₁ = {ana, bilal, chen, dana}. Later export F₂ = {ana, chen, dana, eli}.
- F₂ \ F₁ = {eli} — one arrival.
- F₁ \ F₂ = {bilal} — one absence. Bilal may have unfollowed you. Bilal may also have deleted the account, been suspended, or renamed to "eli" — in which case both sets lied in opposite directions and the truth is zero change.
Four-element sets make this look trivial; at 40,000 followers the same ambiguity applies to every element, which is why reporting both numbers with their caveat attached is the entire game.
What we let the UI say
The rules we ended up with, stated as UI copy constraints:
- Current-state language for single snapshots ("doesn't follow you back"), change language only for snapshot pairs.
- Historical departures are "may have unfollowed," always — no exceptions for "obvious" cases, because the obvious cases are where the rename trap lives.
- Exact dates are never attached to a departure. The export dates bound the window; that's all that can be claimed.
- The limits ship next to the results, not in a FAQ nobody opens.
None of this requires exotic engineering — a Set, a normalize function, and the discipline to label outputs by what was actually computed. But it does require deciding, before you write the results screen, whether you're building a measurement tool or a story generator. Follower data is emotional; people read a departure list as a list of rejections. The least a tool owes them is not dressing set absence up as human intent.
Follower Change applies this model to the official exports of Instagram, TikTok, X, Threads, YouTube, Facebook, and LinkedIn — comparing snapshots locally in the user's browser, with the files never leaving the device.
Top comments (0)