Client engagement · Backend & Full-Stack Engineer · Jul 2026 — Jul 2026
EVO
Field Merchandising Route Planner
Completed, cancelled before launch
What mattered
Schedules are computed from a baseline plus expiring patches instead of mutating the baseline.
- effective schedule = baseline ⊕ active patches. The baseline stays unchanged, so there is nothing to undo.
- MoveVisit is a distinct patch type from TimeShift, so the two stay independently expirable.
- Quota, revenue and fairness constraints are validated live while dragging, not surfaced after a complaint.
- A four-object legacy chain collapsed to three, with the loss proved zero from assignment history.
System map
Context
Route planner for field merchandisers: a supervisor assigns which stores get visited, how often, and what happens at each stop. I did the domain modelling and the .NET backend.
Plans change constantly — a closed district, a store ban, someone off sick — and every one of those changes has to be undone later. Nobody remembers.
Making the schedule a computation
The schedule is not stored. A stable monthly baseline plus a set of patches with expiry dates, and the effective plan is computed:
effective schedule = baseline ⊕ active patches
A patch for a summit expires and the plan reverts itself. There is no undo step to forget, and no drift between what the table says and what is true, because the table only holds the baseline.
MoveVisit was added as a distinct patch type rather than overloading the
existing TimeShift, so that the two remain independently expirable — the
alternative collapses a "moved this visit" and a "shifted the whole day" into
one record that cannot be reverted separately.
Deleting an abstraction
The legacy system chained four objects: seat (koltuk) → route → point list → schedule. The seat existed only to decouple the job from the person holding it.
I collapsed it to three — person, route, tasks — and had to argue that nothing was lost. It wasn't: the assignment history already carries what the seat was there to preserve (turnover per route, mobbing signals), without a fourth object every query has to join through.
Same instinct elsewhere: store status is derived from route membership, not
a flag planners toggle — a store on no route is unassigned, which is an
operational state rather than a commercial one. And "campaign" stopped being a
concept at all once TaskTemplate carried target and valid_until.
Constraints checked while planning, not afterwards
The 450-minute quota, the revenue threshold and store-mix fairness are validated live as the planner drags visits around, rather than surfaced when someone complains. That pushes the rule engine onto the read path, so it had to be cheap enough to run on every interaction.
The planner narrows, ranks and previews the impact of a change; the human chooses. The publish gate will let a plan through with errors, but only with a written justification attached.
Contract-first
The .NET backend emits an OpenAPI spec, and that spec generates the React client. A breaking response change fails at build time instead of turning into a browser bug.
Analytics are computed on read, and route_change_log is a facade over the
existing audit_log rather than a second table telling a slightly different
story.
A rebuild I stopped
A React rebuild of the planner was underway. I stopped it and hosted the working prototype directly, with the backend wired behind it. That deleted 4,552 lines and the 107 tests covering them.
Those tests had been passing and reporting coverage on code that was no longer used: a test suite that made the project more confident and less correct at the same time.
Result
The planner was finished and the demo is that build. The engagement was cancelled before launch by a decision on the client's side, so none of it ran in production.
Screens


