Sports, Fitness & Athletics// diagnostic

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. Count the buckets and record them before forming a theory. In a large export, a theory formed first will find evidence for itself.
  6. 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

ClassWhat you seeWhat it meansWho fixes it
Present in 1 list onlyA person in the booking tool and nowhere elseA join that never propagated, or a self-signup that never became a paying memberWhoever owns the joining path
Present everywhere, status differsActive in one, lapsed or frozen in anotherTwo definitions of active, not a sync failureWhoever gets to define active
Two rows, one humanSame person under 2 emails, or a changed surnameDuplicate identity, usually created at self-signupA merge procedure, then a uniqueness rule
In no system, but trainingSomeone on the paper register who appears in nothingA walk-in or a WhatsApp joiner nobody enteredThe front desk process
The 4 mismatch classes and who can fix each

The causes, ordered by how cheaply you can rule each one out

CauseSignatureCheapest checkFix
No declared system of recordEach list holds rows the others lack, in both directionsAsk 3 staff which system they would trust. Disagreement is the diagnosisDeclare one per object, in writing
One-way syncJoins propagate, leaves do not — or the reverseCompare the newest join date against the newest leave date in each systemAdd the missing direction or a nightly reconciliation job
Different definitions of activeCounts differ by roughly the number of freezes and arrearsCount frozen and in-arrears members and see whether the gap matchesA shared status vocabulary, written down
Soft deletesRemoved members still present in one exportLook for an archived or inactive column the export ignoresFilter on export and agree what deleted means
Duplicate self-signups2 rows sharing a phone number or a date of birthGroup by normalised phone and count groups above 1Merge, then enforce uniqueness at signup
Family and guardian modellingUnder-18s counted as members in one system, dependants in anotherCount records with a date of birth under 18 in each systemDecide whether a child is a member or a linked person
Trials and prospectsThe booking tool counts anyone who ever booked anythingFilter to members with at least 1 successful paymentSeparate a prospect record from a member record
Causes, their signature, and the check that confirms them

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 usedWhat it means thereWho it counts that others do not
Booking toolAnyone allowed to book right nowTrials, guests, and pack holders with unused credits
Billing platformAnyone with a live mandate and no cancellationFrozen members still paying a reduced amount; excludes anyone paying by transfer
Door or access controlAnyone whose credential opens the barrierStaff, coaches, and ex-members whose card was never revoked
The spreadsheetWhatever the person maintaining it decidedWhoever they remember, and nobody they have not seen lately
What active means in each system, and who it silently includes

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

  1. Mismatches dominated by status disagreement? Write the status vocabulary. This is a definition exercise and needs no new software.
  2. Dominated by direction — joins arriving, leaves not? Build a scheduled reconciliation job that reports differences, and fix the sync path it exposes.
  3. 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.
  4. 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.
  5. 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
// shipped work

The work behind this page

Builds from our portfolio that this page draws on.

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