How I Keep Pay, Promotion and Retirement Rules in Step With Changing Regulations
On this page
- Introduction
- A. A system that acts on the law
- B. Where the rules actually live
- C. The approaches I rejected
- D. Twelve obstacles in the way
- Method
- A. Reading the regulation, and citing it next to the rule
- B. Salary tables as data with an effective date
- C. Checking each table against the one it replaces
- D. Two employment types, two lines of regulations
- E. Loading the tables once
- F. The next salary step comes from the table
- G. Promotion with its conditions in order
- H. Retirement age from the position
- I. The system suggests, an administrator decides
- J. Each score judged by its own era
- K. Legal status that follows the events
- L. Decrees whose figures come from the records
- Results and Discussion
- A. What changes when the next regulation arrives
- B. Why the citations matter as much as the code
- C. Where this approach fits
- Conclusion
- Terms
Introduction
A. A system that acts on the law
A personnel system for civil servants does more than store records. It tells an employee when their next periodic salary step is due, when they become eligible for promotion, on which date they retire, what their basic salary is and which members of their family qualify for an allowance. Every one of those answers is set by regulation — government regulations, presidential regulations, laws and the rules of the national civil-service agency — not by the organisation that runs the system.
I am building such a system for a city government, as the replacement for an older one built by another team. The old system had answers too, but many of them had been written into its code years ago and had quietly gone out of date as new regulations replaced old ones. A calculation that was right when it was written becomes wrong the day a new regulation takes effect, and nothing in the code announces that it has happened.
The challenge is to keep pay, promotion and retirement rules in step with the regulations that define them: correct for the date each calculation is about, and still correct after the next regulation is published.
Both halves of that sentence matter. "Correct for the date" means that the salary an employee earned in 2018 is calculated under the regulation that applied in 2018, not under today's table, because historical records, decrees and corrections all refer to the past. "Still correct after the next regulation" means that a new salary table or a new allowance rule should be something I add, not something that forces me to rewrite and retest the code that uses it.
B. Where the rules actually live
The source of truth for these rules is not a specification document; it is a set of regulations published by the state, often amending earlier ones. The basic salary of a civil servant comes from a regulation first issued in 1977 and amended many times since, each amendment replacing the salary table in its appendix. Contract-based government employees are paid under a different presidential regulation with its own amendments. The allowance for a spouse and children comes from yet another pair of regulations. Retirement age, promotion requirements and performance assessment each have their own.
So before I could write any rule, I had to read the regulations themselves — the articles that set a condition and the appendices that hold the tables — rather than infer the rules from what the old system happened to do. Reading the old code told me what had been implemented; only the regulations told me what was required, and where the two disagreed, the regulation won.
C. The approaches I rejected
The simplest approach is to write each rule as code with its numbers inside: a salary table as an array, "two years" for the next salary step, "four years" for promotion. I rejected it because every new regulation would mean editing code, and the old values would disappear the moment they were replaced, taking every historical calculation with them.
The opposite approach is a general rules engine where every condition is configuration. I rejected that too. The arithmetic of these rules — counting months of service, rounding a date to the next promotion period, comparing two spouses' salaries — is clearer and safer as tested code than as configuration that nobody can read in one place. The approach I chose sits between the two: what changes when a regulation changes is data with an effective date, and what stays the same is code that cites the regulation it implements.
D. Twelve obstacles in the way
Easy to state, awkward to build. Twelve things stood between the regulations on paper and a system that applies them correctly.
The first is that the rules in the old system are out of date, and the true source is the regulation itself. Copying the old system's behaviour is the rejected alternative, because it would faithfully reproduce rules that have already been replaced.
The second is that salary tables change with each regulation, and each one applies from a particular date. Replacing the old table with the new one is the rejected alternative; it would make every past calculation wrong.
The third is that a regulation's appendix has to be transcribed, and one mistyped cell is invisible. A salary table has hundreds of cells, and trusting a careful transcription is the rejected alternative.
The fourth is that permanent civil servants and contract employees are governed by different regulations. Treating them as one salary scheme with a few exceptions is the rejected alternative.
The fifth is that these calculations run for every employee at once. A list of upcoming due dates asks the same questions of tens of thousands of employees, and looking up the tables once per employee is the rejected alternative.
The sixth is that the next periodic salary step is not simply "two years later". A fixed interval is the rejected alternative; the step depends on the salary table itself.
The seventh is that promotion eligibility has layered date conditions. A single "four years after the last promotion" is the rejected alternative, because penalties, the highest rank and retirement all change the answer.
The eighth is that retirement age depends on the position. One retirement age for everyone is the rejected alternative.
The ninth is that allowance eligibility depends on family facts the system cannot fully trust. Letting the system grant or withdraw allowances on its own is the rejected alternative.
The tenth is that the way performance is assessed has changed several times. Interpreting every old score under today's method is the rejected alternative.
The eleventh is that an employee's legal status changes automatically because of events the regulations define. Asking administrators to set and reset it by hand for every leave or assignment is the rejected alternative.
The twelfth is that official decrees must cite their legal basis and contain figures that agree with the records. Typing those figures into a document template is the rejected alternative.
The order runs from the source outward. The first is about where the rules come from. The second to fifth are about the salary tables, which almost everything else depends on. Within that group the order matters as well: a table has to be dated and correct before it can be checked against its predecessor, split by employment type, or loaded once for every employee. The sixth to eleventh are the rules that use those tables and the employee's history, and the last is about the documents in which the rules finally reach people.
Method
A. Reading the regulation, and citing it next to the rule
Here is the answer to the first obstacle: I read the regulations directly — the articles for conditions, the appendices for tables — and every rule in the system cites the regulation it implements, either in the reference table that holds it or in the comment above the code that applies it. When a rule is questioned, the answer is a regulation number, not a recollection of how the old system behaved.
B. Salary tables as data with an effective date
For the second obstacle, each salary regulation is a row with the employment type it applies to, its number and the date it takes effect, and each regulation has its own table of salaries by rank and years of service. To find a salary on a given date, the system takes the newest regulation in force on that date, then the row with the largest years of service not exceeding the employee's.
A salary in 2018 is therefore looked up under the 2015 table and a salary in 2024 under the 2024 table, with no special cases, and adding the next regulation means adding a row and a table.
Every amount in these tables is stored as an integer in the currency's minor unit, the money convention I keep across all my systems:
C. Checking each table against the one it replaces
The third obstacle is answered by letting the regulations check each other. Each amendment raised every salary by a fixed percentage — five per cent in one, eight per cent in the next — so every cell of a new table must equal the matching cell of the old one plus that percentage, within the rounding the regulation applies. Salaries must also rise monotonically with years of service and with rank. A transcription error breaks one of those relationships, and the check fails before the data is ever used.
D. Two employment types, two lines of regulations
For the fourth obstacle, the employment type is a column on the regulation and on the rank. Permanent civil servants and contract employees each have their own ranks and their own chain of regulations, so a lookup never mixes them and a new regulation for one never affects the other.
E. Loading the tables once
For the fifth obstacle, the regulation, rank and salary tables are small and rarely change, so each process loads them once and answers every lookup from memory. Without that, a list of due dates for every employee would run several queries per employee — tens of thousands of queries for one page. Any change to those reference tables clears the loaded copy, so the memory never serves an outdated table.
F. The next salary step comes from the table
The sixth obstacle is answered by asking the salary table, not a constant. The next periodic step is the first day of the month in which the employee's years of service in their rank reach the next level that the table for their rank actually lists. If a regulation changes where those levels are, the due dates follow it without any code change, and the amount of the new step is read from the table rather than typed.
G. Promotion with its conditions in order
For the seventh obstacle, the promotion due date applies its conditions in sequence: the effective date of the current rank plus four years, or five if a disciplinary penalty was imposed since then; rounded forward to the next promotion period in force; no due date at the highest rank; and none on or after the employee's retirement date. Each employee who becomes due is notified by e-mail.
H. Retirement age from the position
The eighth obstacle is answered by deriving the retirement age from the position the employee holds: first the age set for that position in the city's position map, then the age in the national position catalogue, and otherwise the default of 58 years. Retirement takes effect on the first day of the month after the employee reaches that age. The result is stored as a derived date and recalculated whenever the birth date or position changes, and once a day for positions dated in the future.
I. The system suggests, an administrator decides
For the ninth obstacle, the family allowance rules are implemented as an advisor rather than a decision. One living spouse from a marriage that is still in force qualifies; if both spouses are civil servants, only the one with the higher basic salary receives it. At most two children qualify, or three if the third was born before 1 March 1994, and a child must be under 21, or up to 25 while still in education. For each family member the advisor answers yes, no or "needs review", with the reason.
The decision stays with an administrator, because the facts the rules depend on — whether a child still studies, whether a spouse works elsewhere in government — are often out of date. A suggestion with a reason makes the administrator faster without letting stale data decide someone's pay.
J. Each score judged by its own era
The tenth obstacle is answered by a table of assessment eras: the older appraisal system up to 2013, the performance target system of 2014 to 2021, the transition year under the 2019 regulation, and the rating matrix in force since 2022. Each era records the years it covers and how it assesses, so a score is interpreted under the method that produced it. Where a regulation's own ranges overlap, the system does not pick a rating automatically; that judgement stays with a person.
K. Legal status that follows the events
For the eleventh obstacle, events that the regulations say change an employee's legal status — unpaid leave outside state service, study assignments, dismissal, transfer out — set that status for the period they cover and restore it afterwards. Which kinds of leave have that effect is a flag on the leave type in its reference table, not a condition in code. A status that an administrator has set by hand is never overwritten by these automatic changes.
L. Decrees whose figures come from the records
The twelfth obstacle is answered by generating draft decrees from the records rather than typing them. A periodic salary step decree cites the regulations it rests on and takes every figure — rank, years of service, old and new salary, effective date — from the data the rules above produced. Decrees that only the national agency may issue, such as promotions and pensions, are uploaded as received rather than generated.
Results and Discussion
A. What changes when the next regulation arrives
When a new salary regulation is published, the work is now to transcribe its appendix into a new table, add one row with its effective date and run the cross-check against the table it replaces. No calculation needs rewriting: past salaries keep using the tables of their time, and future due dates and salary steps follow the new table automatically.
B. Why the citations matter as much as the code
Every rule in the system can now be traced to a regulation. That has proved as useful as the calculations themselves: when a result is questioned, the conversation is about what a regulation says, not about what a program does, and the people responsible for personnel can check the source themselves.
C. Where this approach fits
Separating data that changes with regulation from arithmetic that implements it suits any domain governed by published rules that are amended over time: payroll, taxation, social benefits. It does not remove the need to read the regulations; it makes reading them the main task, and keeps the result of that reading in one place where it can be checked.
Conclusion
Rules that come from regulations do not belong to the code that applies them, and they do not stay still. Keeping them correct came down to one separation: what a regulation changes — salary tables, assessment eras, the effect of a leave type — is data with an effective date, and what stays the same is arithmetic, written as code that names the regulation it implements.
The habit I would carry to any regulated system is to read the regulation itself rather than the system that came before. The old code shows what someone once decided; the regulation shows what is required now, and a system that cites it can be checked by the people it serves.
Terms
- Periodic salary step
- A salary increase within the same rank, due when an employee's years of service reach the next level of the salary table.
- Effective date
- The date from which a regulation applies; earlier dates are still governed by the regulation it replaced.
- Retirement age
- The age at which an employee must retire, which for civil servants depends on the position they hold.
Technologies covered
- PHP