Gino Kaleb gino kaleb SYS ADMIN
/

A rules engine for academic requests

I automated request approvals with a layered rules engine. The most interesting part was deciding which cases the system resolves and which ones go to a human.

When I started designing the decision engine for “Project Phoenix”, a system to manage academic requests, the task looked purely technical: automate request approvals, something that until then depended on manual reviews, paperwork, and whoever happened to be evaluating that day. I was thinking in services, entities, and endpoints. The real problem turned out to be something else: how to turn a procedure that people used to evaluate into rules a system can apply without being unfair.

Why two rules weren’t enough

The first temptation was to solve it with a couple of conditions: if the GPA is above 8.5, approve; otherwise, reject. No debts, proceed; debts, stop. Quick to implement and technically functional.

It would also be unfair. A system like that can’t tell the difference between a first-semester student who is still adjusting and a final-semester student with a solid record. It doesn’t understand that a “schedule conflict” is a failure of the scheduling system itself and shouldn’t penalize anyone, while a “health issue” is a case no machine should judge automatically.

I wasn’t building a simple validator: I was replacing a person’s judgment with code. And for the outcome to be fair, that code needed context.

The layers of a decision

I structured the evaluation engine as a series of filters, each with its own purpose, following the order in which a reasonable person would review a case.

Eligibility

The outermost, simplest layer: the binary rules. Is the request period open? Has the user hit their request limit? This filter doesn’t judge, it just checks requirements. If you don’t meet them, you don’t get in.

Viability

Once inside, the request faced the non-negotiable conditions. Is the student’s academic status “active”? Any serious administrative holds? A “no” here meant near-immediate rejection: these are the cases the regulations make unviable from the start.

Context

This was the interesting part. Instead of treating every reason the same, I wrote specific logic for each one:

  • A schedule conflict was almost always approved. It’s a logistics problem the system should fix, not judge.
  • A work situation was weighed together with the semester. For an advanced student, balancing work and school is normal; in the early semesters it can be a sign that the case deserves a closer look.
  • A health issue marked the limit of automation: the decision was not to decide. The request went straight to human review.

The third state: “Pending review”

The most important design decision was adding a state beyond “Approved” and “Rejected”: “Pending review”.

It was the system’s escape valve. Borderline GPAs, ambiguous reasons, and combinations of factors with no clear conclusion landed there, with all the information already processed and ready for a person to look at. In practice this took roughly 80% of the routine, predictable cases off the administrative staff’s plate and left them the 20% that actually needed human judgment.

In the end, that’s what the Project Phoenix engine came down to: a logical flow that resolves the predictable and sets aside what isn’t its call. The goal was never to replace human judgment, just to filter the noise so it gets used where it’s needed.