Every BES operator I have worked with runs the same query pattern: at the moment somebody asks whether a participant is eligible to retire, the pipeline computes it. It reads the current birth date, the current contribution start date, the current sum of paid periods, and returns a boolean. Clean. Deterministic. Auditable in the sense that you can rerun it and get the same answer.
Except it is not auditable in any way that matters. Because the answer you get today is not the answer the same query produced eighteen months ago, and neither of you knows why.
Eligibility Is Not a Calculation, It Is a Legal Event
BES retirement eligibility in Turkey rests on two interlocking conditions: the participant must have been in the system for at least ten years of contribution, and must have reached the age threshold defined by the regulation in force when they enrolled. Both inputs sit upstream of your warehouse. Both change.
- Birth dates get corrected when a participant updates their nüfus record
- Contribution start dates shift when a transferred fund from another pension company gets reclassified
- Paid-period counts recalculate when a suspended contribution is retroactively marked as valid
- The regulation itself reinterprets what counts as a "contribution year" when SGK data reconciles
Each of these is a legitimate upstream correction. None of them are bugs. And every one of them silently rewrites who was eligible on a date in the past.
If your pipeline computes eligibility at query time, you have no record that on 14 March 2021, participant X was eligible under the rules as they were then understood, using the birth date as it was then recorded. You only have the answer for today. And if a participant walks in with a printout from 2021 saying they qualified, you cannot reproduce that printout. The system that produced it no longer exists in a queryable form.
What This Looks Like at the Counter
A participant arrives at a branch. Sixty years old, enrolled in 2013, expecting to retire on the ten-year rule. The operator runs the query. The system says: not eligible. Contribution duration insufficient.
What happened? Two years ago, a data correction reclassified an eighteen-month period of contributions from an acquired portfolio. Those months were originally credited as BES contributions. During the migration cleanup, they were reclassified as transfer-in balance, which counts toward accumulated capital but not toward the ten-year eligibility clock. Nobody flagged the participant. Nobody sent a letter. The pipeline just started returning a different answer.
The participant is not wrong. The pipeline is not wrong today. But there is no way to reconstruct the fact that between 2013 and the reclassification, the system was telling this participant — through every annual statement, every mobile app query, every branch inquiry — that they were on track.
Why Standard Pipelines Fail Here
Most BES data pipelines I have audited share the same architecture: nightly ETL loads current-state tables, eligibility functions read from current-state, regulatory reports aggregate the outputs. The eligibility function is a pure function of current-state inputs.
The structural problem is that this treats a legal question as a mathematical one. Whether someone qualifies for retirement under BES rules is not a fact about numbers in a table. It is a fact about what the state of the world was, according to your records, at each moment in time. That is a bitemporal problem, and almost nobody models it bitemporally.
What is missing:
- Valid-time for every eligibility-relevant field (when did this birth date apply in the real world?)
- Transaction-time for every correction (when did our system come to believe this?)
- Recorded eligibility events, not derived ones — when a participant crosses a threshold, that crossing gets written down, timestamped, and never recomputed
- Notification hooks that fire when a retroactive change would have altered a past eligibility state
Without these, the regulator reports you send to EGM are internally consistent for the snapshot date, but they are not consistent with the reports you sent last quarter, and you cannot explain the difference.
The Fix Is Boring and Structural
Stop treating eligibility as something you compute. Start treating it as something that happens.
When a participant's state changes such that they now cross an eligibility threshold — under the birth date and contribution history as currently recorded — write an event. Timestamped. Immutable. Reason-coded. When a retroactive correction would have moved a past crossing, write a compensating event and flag it for review. The current-state view remains derivable, but it is now derivable from a log, not a table.
This costs you three things:
- Storage, which is negligible
- A rewrite of the eligibility service, which is a two-quarter project if you are honest about the scope
- Political capital, because it forces the business to define what happens when a past eligibility gets retroactively withdrawn — a question everyone has been happy to leave undefined
The third one is why this never gets built. Not because the engineering is hard, but because event-sourced eligibility exposes decisions the operations team has been quietly deferring for a decade. Who owes whom what, when the pipeline changes its mind about someone's past?
What to Ask Your Team on Monday
If you run BES data operations, three questions:
- Can you reproduce, exactly, the eligibility answer that any participant received on any given date in the past five years?
- When an upstream correction changes a past eligibility state, does anyone find out before the participant does?
- Is your eligibility logic a function, or is it a log?
If the answers are no, no, and function — you do not have an eligibility system. You have a report generator that happens to answer eligibility questions, and it will keep answering them differently every time the data underneath shifts. The participant standing at the counter is not a bug report. They are the first person who noticed.