Amir TabatabaeiWork with me
← All work

Client engagement · Backend & Full-Stack Engineer · Jul 2026 — Jul 2026

EVO

Field Merchandising Route Planner

Completed, cancelled before launch

.NET 10ReactTypeScriptSQL ServerMinIOPlaywright

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

CLIENTSURFACEHTTPSEF CORES3COMPUTEE2EOpenAPI contractPlanner UIEvo APIBaseline + patchesSQL ServerMinIOPlaywright

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

The planner workspace: routes on the left, two routes drawn across a city map, week navigation, and the effective-versus-baseline toggle
A route selected: the week's visits with durations and drive times between stops, revenue against target, active patch count, assignment history, and a status bar counting errors, warnings and workload fairness across merchandisers
The same plan as a table