If you build payroll software for India, the last 18 months have been about quietly rewriting your wage definition logic before customers notice. The four new Labour Codes consolidate 29 legacy acts. Most of the customer-facing surface looks similar. The internal calculations are not.
I want to walk through what changed for engineers actually maintaining a payroll system, what stayed the same, and where the edge cases will surface in the next 6-12 months. This is written for engineers and HR-tech builders, not for HR managers.
The four codes in one paragraph
The Code on Wages 2019, the Industrial Relations Code 2020, the Social Security Code 2020, and the Occupational Safety, Health and Working Conditions Code 2020 replace the older fragmented framework. The Codes have been notified at the centre but are still rolling out across states for the rules layer. The substantive definitions, especially of "wages" and "employee" and "establishment", have changed in ways that ripple through every payroll line item.
The single biggest thing that changed: the definition of wages
The Code on Wages defines wages as basic pay plus dearness allowance plus retaining allowance, and it caps the excluded allowances at 50% of total remuneration. That 50% cap is the line that quietly broke half the existing payroll systems.
Before the Codes, a company could structure a salary as 30% basic and 70% allowances, which kept the statutory contribution base low. After the Codes, if the allowance portion exceeds 50% of total remuneration, the excess gets added back into wages for the purpose of contribution calculations.
In code terms, the change looks something like this for the statutory base calculation:
def compute_statutory_wage_base(salary_components):
basic = salary_components.get('basic', 0)
da = salary_components.get('dearness_allowance', 0)
retaining = salary_components.get('retaining_allowance', 0)
base_wages = basic + da + retaining
all_components = sum(salary_components.values())
excluded_allowances = all_components - base_wages
# The 50% cap: if excluded allowances > 50% of total, add the excess back
cap = 0.5 * all_components
if excluded_allowances > cap:
excess = excluded_allowances - cap
base_wages += excess
return base_wages
The implication for PF, ESIC, gratuity, and statutory bonus is that the contribution base is now higher for any employee whose previous structure leaned heavily on allowances. We had customers whose monthly employer PF contribution went up by 12-18% the day they applied the new definition correctly. That is not a bug. That is the law catching up to the structure.
If you are running a payroll system and you have not yet rewritten this function for the new wage definition, your output is wrong and you do not know it yet.
What changed for PF specifically
The Social Security Code retains the EPFO framework but cleans up the definition of "establishment" and "employee" and aligns the wage base with the new wage definition. The contribution rates have not changed in the Code. The wage base on which the contribution is computed has, because of the 50% cap above.
Two practical things to watch for in payroll code:
The first is the wage ceiling. The statutory PF ceiling sits at INR 15,000 per month for mandatory coverage, with optional coverage above it. The Codes do not change this number, but the wage definition feeding it does, and the choice of apply_ceiling=True vs False in your contribution function is now a more consequential choice for any salary structure that leans on allowances.
The second is the eligibility threshold for an "establishment". The old PF Act applied at 20 employees. The Social Security Code retains this threshold for organized-sector EPFO coverage, but expands the gig and platform worker definitions for the corresponding social security schemes. If your payroll system was designed for organized-sector salaried workers only, the platform worker case is now a real engineering surface you will need to handle.
What changed for ESIC
ESIC retains its existing rate structure under the Social Security Code. The wage ceiling for ESIC eligibility remains at INR 21,000 per month. The wage definition for the ESIC contribution base, however, follows the new Code wage definition, which means the same 50% cap logic applies.
If your system already had separate "PF base" and "ESIC base" functions, you are likely fine with a single wage-base function that applies the new definition. If your system was using gross-salary-minus-HRA as the ESIC base, you have legacy logic that needs to be retired.
What changed for gratuity
Gratuity continues to be governed by the Payment of Gratuity Act framework, now subsumed into the Social Security Code. The eligibility threshold of 5 years of continuous service is unchanged. The calculation formula of (last_drawn_wages * 15 * years_of_service) / 26 is unchanged.
What did change is the definition of last_drawn_wages for the calculation. It now follows the Code wage definition. For most employees this does not change the gratuity amount. For employees whose salary structure had a high allowance ratio, the gratuity accrual base is higher, and the gratuity payable on exit is higher too.
The practical engineering implication is that your monthly gratuity accrual amount, if you compute one for accounting purposes, needs to use the new wage definition. If you are tracking this on a per-employee basis in your ledger, the accrual amount for many employees should have stepped up the month the new wage definition was applied.
What changed for bonus
The Payment of Bonus Act continues to apply. The minimum bonus at 8.33% and maximum bonus at 20% of the calculated bonus wages are unchanged. The eligibility threshold remains at INR 21,000 per month. The wage definition for bonus calculation, again, follows the Code definition.
For most companies that pay a Diwali bonus regardless of statutory minimum, the practical change is small. For companies that compute the exact statutory bonus, the calculation base shifts the same way it does for the other contributions.
State-level complexity is the place that bites
The four Codes are central legislation. The rules implementing them sit with each state government. As of mid-2026, the rule notification status varies. Some states have published full rules. Others have published draft rules. Others have not yet started the rule-making process and are operating under transitional provisions.
For payroll software, this means the implementation of the new Codes is not a single switch you flip. It is a state-by-state rollout where you maintain a configuration table of which state has notified which rules under which Code, and your wage and contribution functions consult that table at calculation time.
In practice, the engineering shape looks like:
def get_applicable_rules(employee_state, code_name, calculation_date):
"""
Returns the rule version applicable for the given state, code, and date.
Defaults to transitional/legacy rules if state has not notified.
"""
state_rules = STATE_RULE_NOTIFICATIONS.get(employee_state, {})
code_rules = state_rules.get(code_name)
if not code_rules:
return LEGACY_RULES[code_name]
for rule_version in code_rules:
if rule_version['effective_from'] <= calculation_date <= rule_version.get('effective_to', '9999-12-31'):
return rule_version
return LEGACY_RULES[code_name]
Most legacy payroll systems in the Indian market do not have this state-level configuration layer. They have hardcoded rates that change centrally with a software update. The Code transition forces a more configurable architecture, and the engineering work to retrofit that into a system that was not designed for it is non-trivial.
Where the edge cases will surface
Three places to instrument heavily.
The first is multi-state employees. A salesperson who is registered in Karnataka but spends three weeks of every month in Maharashtra has different professional tax obligations in each state, and under the Codes, the rules-applicability table needs to be evaluated for each state-and-period combination. This is where most calculation bugs will hide.
The second is mid-month joiners and leavers with the new wage definition. The proration logic for the wage base has to be applied before the 50% cap is evaluated, not after. We have seen at least one off-by-one bug in our own pipeline because the proration was applied to the wage base in the wrong order. Worth writing explicit tests for this case.
The third is the gig-worker boundary. The Social Security Code introduces categories like "gig worker" and "platform worker" with their own social security scheme. The boundary between a contractor, a gig worker, and an employee is a legal call, not a code call, but your payroll system has to honour the boundary cleanly once it is decided. If you have a single "worker" entity in your data model, you may need to add a worker_classification enum and route the contribution logic accordingly.
What we learned running this for live payrolls
Three practical lessons from running the new wage definition across our live customer payrolls in the last 6 months.
First, expect at least a 10-15% step-up in employer statutory cost for customers whose previous salary structures leaned on allowances. Communicate this before the first payslip lands, not after.
Second, the gratuity provision on the balance sheet should be recalculated with the new wage base. Several of our customers' finance teams had not updated their gratuity provision, and the variance surfaced during audit.
Third, the configuration of which state has notified which rules under which Code is itself a data product. We treat it like a release artifact, with version control and a clear audit trail. Anyone building India payroll software should treat their rules-configuration table the same way.
Where to read more
The full statutory walkthrough, with the specific contribution rates by state and the historical context for why the Codes are structured the way they are, sits on the India EOR corridor page.
For engineering teams thinking about building or buying India payroll capability rather than running it yourself, the broader Asanify platform overview walks through the API surface, the data model for multi-state and multi-classification workers, and the audit trail patterns we use.
The Labour Codes are not a minor compliance update. They are a quiet rewrite of the wage definition that affects every contribution calculation downstream. If you are building or maintaining payroll software for India, the engineering work is meaningful, and the half-life of "we will get to it" is shortening fast.
Top comments (0)