Three systems, three member lists, all wrong in different places
In short
Export every list on the same day at the same hour, reconcile them on a key that exists in all three, and classify each mismatch before anyone argues about the count. The gap is rarely one bug: it is a mixture of differing definitions of active, joins and leaves that travel one way only, soft-deleted rows and duplicate self-signups — and each of those has a different fix.
Key takeaways
- Export all lists within the same hour. Exports taken days apart differ by real events, not by defects.
- Classify every mismatch before totalling. The class names the cause; the count names nothing.
- Most gaps are a definition disagreement about the word active, not a broken sync.
- Duplicates almost always originate at self-signup, so dedupe once and add a uniqueness rule, or they return.
- Declare a system of record per object — people, bookings, money — not one winner for the whole club.
Three lists, three counts, and a meeting about which one is right. Skip the meeting. Export each list on the same day within the same hour, reconcile them on a key that exists in all three, and sort every mismatch into 4 classes. The class tells you the cause; the totals tell you nothing except that a problem exists.
This is a diagnosable defect, not evidence that the platform is wrong. Almost every club running a booking tool, a payment platform and a spreadsheet has this, including clubs whose software is working exactly as designed, because the 3 systems were never told which of them decides what a member is.
The check: same day, same hour, one key
- Export all 3 lists within the same hour, with every status field included. A club with 400 members and 30 joins a month changes about 1 record per working day, so exports taken a week apart differ by real events before any defect is involved.
- Choose the join key. In order of preference: a shared member id, then a normalised mobile number, then email, then name plus date of birth. Write down which one you used, because the answer changes what the reconciliation can prove.
- Normalise before joining. Lowercase emails, strip spaces and country-code variants from phone numbers, trim trailing whitespace. A meaningful share of apparent mismatches disappear at this step alone.
- Join, then bucket every row into: present in all 3 and agreeing, present in all 3 but disagreeing on status, present in some but not others, or duplicated within a single list.
- Count the buckets and record them before forming a theory. In a large export, a theory formed first will find evidence for itself.
- Take 10 rows from each bucket and follow them back to a person. Every reconciliation exercise that skipped this step has ended in an argument about the spreadsheet rather than about the members.
Classify the mismatches, do not total them
| Class | What you see | What it means | Who fixes it |
|---|---|---|---|
| Present in 1 list only | A person in the booking tool and nowhere else | A join that never propagated, or a self-signup that never became a paying member | Whoever owns the joining path |
| Present everywhere, status differs | Active in one, lapsed or frozen in another | Two definitions of active, not a sync failure | Whoever gets to define active |
| Two rows, one human | Same person under 2 emails, or a changed surname | Duplicate identity, usually created at self-signup | A merge procedure, then a uniqueness rule |
| In no system, but training | Someone on the paper register who appears in nothing | A walk-in or a WhatsApp joiner nobody entered | The front desk process |
The causes, ordered by how cheaply you can rule each one out
| Cause | Signature | Cheapest check | Fix |
|---|---|---|---|
| No declared system of record | Each list holds rows the others lack, in both directions | Ask 3 staff which system they would trust. Disagreement is the diagnosis | Declare one per object, in writing |
| One-way sync | Joins propagate, leaves do not — or the reverse | Compare the newest join date against the newest leave date in each system | Add the missing direction or a nightly reconciliation job |
| Different definitions of active | Counts differ by roughly the number of freezes and arrears | Count frozen and in-arrears members and see whether the gap matches | A shared status vocabulary, written down |
| Soft deletes | Removed members still present in one export | Look for an archived or inactive column the export ignores | Filter on export and agree what deleted means |
| Duplicate self-signups | 2 rows sharing a phone number or a date of birth | Group by normalised phone and count groups above 1 | Merge, then enforce uniqueness at signup |
| Family and guardian modelling | Under-18s counted as members in one system, dependants in another | Count records with a date of birth under 18 in each system | Decide whether a child is a member or a linked person |
| Trials and prospects | The booking tool counts anyone who ever booked anything | Filter to members with at least 1 successful payment | Separate a prospect record from a member record |
The word active is the argument, not the count
Most reconciliations end here. Nothing is broken: 3 systems are each answering a different question correctly, and only the label on the answer is shared.
| Where the word is used | What it means there | Who it counts that others do not |
|---|---|---|
| Booking tool | Anyone allowed to book right now | Trials, guests, and pack holders with unused credits |
| Billing platform | Anyone with a live mandate and no cancellation | Frozen members still paying a reduced amount; excludes anyone paying by transfer |
| Door or access control | Anyone whose credential opens the barrier | Staff, coaches, and ex-members whose card was never revoked |
| The spreadsheet | Whatever the person maintaining it decided | Whoever they remember, and nobody they have not seen lately |
Fix this by writing one status vocabulary and mapping each system's native states onto it, rather than by trying to make 3 products agree. The vocabulary needs a defensible boundary for lapsing — the question answered in what counts as a lapsed member — and a rule for freezes, which behave differently in billing and booking by design: what a membership freeze does to the billing schedule.
Three systems disagreeing is not three bugs. It is usually one missing decision about who owns the word member, repeated three times.
The decision: a reconciliation job, or a declared source of truth
- Mismatches dominated by status disagreement? Write the status vocabulary. This is a definition exercise and needs no new software.
- Dominated by direction — joins arriving, leaves not? Build a scheduled reconciliation job that reports differences, and fix the sync path it exposes.
- Dominated by duplicates? Do the identity work first: merge, then add a uniqueness rule at the point of creation. Deduplicating without closing the door means doing it again next term.
- Every list wrong in both directions, and nobody can name the owner? Declare a system of record per object and enforce the write path. This is the only case that justifies structural change.
- The paper register is the only list matching reality? Then the register is your real system, and the software is a reporting layer with a bad reputation — the situation described in whatever the coach writes on the clipboard.
What declaring a source of truth actually commits you to
The declaration is cheap and the enforcement is not. A system of record is the system that owns the write path: new members are created there and nowhere else, status changes originate there, and every other system receives rather than decides. If a member can still be created in the booking tool by a coach on a Saturday, the declaration is decorative.
- Choose per object, not per product. The billing platform is usually the right record for money and mandates; the club system is usually right for people, squads and consents. One winner for everything is how you end up with a payment tool holding safeguarding data.
- Name the write path for each object and remove the others, including the friendly workarounds. This is a process change, and it is the part that gets skipped.
- Keep the reconciliation job running afterwards. Declaring a record does not stop drift; it only makes drift attributable, which is what turns a quarterly argument into a weekly report.
- The same question recurs wherever a second system also holds an authoritative copy — device data is the clearest case, where the phone's health store and a vendor cloud can both claim to be right: phone health store or vendor cloud as source of truth.
One thing this exercise is not: a reason to migrate. Disagreeing lists follow you to a new platform, and they arrive worse, because import turns a duplicate into a permanent record. Clean first, then decide — the identity order in preparing a roster and member file that will import, and the threshold in most clubs should not build custom software. If a move is genuinely happening mid-term, the sequencing constraint is money already collected against sessions not yet delivered: a three-site academy changing platform in week 6.
The reconciliation job itself — 3 scheduled exports, a normalising step, a differences table and an alert when a bucket grows — is a few days of work rather than a project, and it is exactly the kind of thing we build as internal tools and operations work. The rest of this silo sits under build, buy, extend or migrate, inside our sports, fitness and athletics practice.
Frequently asked questions
Short answers to the follow-ups this page tends to raise.
Which system should be the source of truth for club members?
Whichever system owns the write path for that object, decided per object rather than per product. In most clubs that means the club or booking platform owns people, squads and consents, while the billing platform owns mandates, invoices and payment status. The test is not which product is best but where a change can legitimately originate.
Why do our booking and billing member counts never match?
Usually because they are counting different things and both are correct. A booking system counts people allowed to book, which includes trials and credit holders; a billing system counts live mandates, which excludes anyone paying by bank transfer and treats freezes inconsistently. Compare definitions before comparing numbers.
How do we find duplicate member records?
Group on a normalised mobile number first, then on date of birth plus surname, then on email. Phone finds the most in practice because families share email addresses far more often than they share numbers. Review candidate pairs by hand before merging — an automatic merge on a shared family email is how 2 siblings become 1 member.
Should we merge duplicate members or delete one?
Merge, and keep the surviving record's history from both sides. Deletion loses attendance, payment history and any consent captured against the discarded row, and those are the objects you cannot re-create. Record which id was merged into which, so a later question about an old booking still resolves to a person.
- data reconciliation
- source of truth
- member records
- club operations
The work behind this page
Builds from our portfolio that this page draws on.
AP Copilot
An AI accounts-payable copilot that reads invoices, matches them to POs, and routes clean approvals
FintechFactory OS
Production planning and task management for a tier-1 apparel manufacturer — replacing Excel with automated milestone planning, SOP gate enforcement, and real-time visibility.
ManufacturingRead next
- The member export: what leaves your club platform and what staysAn export is not a backup. It is whichever subset of your data the incumbent chose to serialise, and two of the usual omissions can end a migration on their own.definition
- A session series is not eighteen sessions: the recurrence record explainedA series is a rule plus an exception list, not a pile of rows. Every confusing this-event-or-all-events prompt comes from that one storage decision.definition
Working on something in this space?
Tell us where you are in a sentence or two. We'll tell you honestly whether we're the right team, and what a sensible first slice of the work looks like.
Start the conversation