Instead of building a feature for every school's policy, we built eight reusable engines and exposed their settings, so configuration over code turns most requirements into something the school can change itself.
Every school runs differently
Ask a room of school administrators how they run admissions, or bill fees, or number their students, and you will get as many answers as there are people. One bills monthly, another per term, a third mixes cadences per fee head. One issues roll numbers in one format, another in a format that resets every session. One requires a scanned certificate for every staff qualification; another does not bother.
None of these are edge cases. They are the ordinary policy of running a school, and they differ at every single institution. The question is how to serve that variety without the product collapsing under the weight of everyone's exceptions.
The feature-factory dead end
The instinctive way to serve that variety is one feature request at a time. A school wants term billing; a developer adds a term-billing feature. Another wants roll numbers that reset yearly; that is another feature. Multiply by every policy at every school and the product becomes a sprawl of special cases, each slightly different, each needing maintenance, each a place for bugs to hide.
Worse, the school waits on a release for the smallest change. The backlog never shrinks, the codebase only grows, and the product slowly ossifies. So we asked a different question. Not what feature does this school want, but what is the general capability underneath, and can the school configure it themselves?
The inversion, and the eight engines
Seen that way, the per-term fee and the per-month fee stop being two features; they are one billing engine with a cadence setting. Two roll-number formats stop being two features; they are one numbering engine reading a template. Dozens of requirements collapse into a handful of engines with settings on top. Build those engines exceptionally well and most of what a school asks for becomes configuration rather than code.
When stakeholder feedback surfaced dozens of distinct requirements across admissions, staffing, attendance, examinations, fees and communication, they were resolved through eight reusable engines. An approval and policy engine decides whether an action auto-approves or routes to a person. An organisation and scope map ties classes, sections, divisions and departments to their supervisors. A schema and form builder treats fields as data so forms are built rather than coded. Configurable sequences express every identifier as a token template plus a reset policy.
A session lifecycle engine runs the academic-year rollover as draft, review and commit, carrying roll allocation forward. A branded templating layer renders receipts, report cards, statements, certificates and emails in each school's own identity. Per-tenant integrations and secrets let a school bring its own payment and messaging credentials, encrypted and write-only. And an audit and activity spine makes every consequential action attributable to a named actor.
What makes an engine, not a feature
The distinction is worth stating precisely, because it is the whole discipline. A feature answers one school's requirement. An engine answers the shape of that requirement for every school, and exposes the settings that let each one keep its own. An engine has a small, well-guarded surface, a validated configuration; it enforces its own invariants and refuses malformed input.
The billing engine does not care what a school's cadences are, only that a cadence is one of a known set. The numbering engine does not care what the format is, only that its tokens resolve. The specifics belong to the school; the guarantees belong to the engine. A feature is a promise to one school; an engine is a promise to every school, plus the settings that let each one keep its own policy.
The discipline it demands, and the payoff
This approach has a real failure mode worth naming: over-generalisation. It is possible to build a configuration language so expressive that it becomes a programming language only the vendor can operate, at which point you have simply moved the bespoke work rather than removed it. We guard against that in two ways. Engines expose settings an administrator can actually reason about, so an approval rule reads like a sentence and a number format previews live as you build it. And every configuration is validated against the same rules the platform enforces at runtime, so an invalid setting is caught the moment it is authored, not in production.
The dividend shows up everywhere at once. Onboarding a new school is configuration, not a fork of the product. A school changes a rule and nothing has to be redeployed. There is one place to reason about each kind of behaviour, so understanding one school teaches you all of them. The eight engines are the reason a platform spanning admissions to accounting stays coherent, and the reason the person running the school, not the vendor, holds the controls. The goal was never maximum flexibility; it was the right flexibility, safely operated.

