At a passenger river-transport operation with a garage in Barcarena, in the Brazilian Amazon, every vehicle in the fleet generates maintenance data all the time: tire tread depth, tire pressure, alignment, oil and filters, lubrication. That's a good thing — it means there's enough data to decide with information instead of gut feeling. The problem is that raw data isn't a decision. Someone still has to open 20 spreadsheet tabs, compare category by category, and decide off the top of their head which vehicle is most urgent. In practice, that turns into one of two things: an experienced mechanic deciding by instinct (which works fine until they take vacation or change jobs), or a list nobody looks at until a tire blows out on the road.
This article is about how I modeled that problem — it's not a code tutorial, it's about the engineering decisions behind two small, tested pieces: a wheel wear classifier, and a fleet priority engine. In my day-to-day work I solve this kind of problem with low-code — n8n, Google Apps Script, Google Sheets formulas — because that's what delivers value fastest to whoever actually has to use it. For this article and the public repository, I rewrote the same logic in plain Python: it's easier to read, test, and adapt for anyone not using my specific stack. The code is published, generalized and anonymized, at github.com/siandrosena/fleet-maintenance-priority-engine.
The first problem: the mechanic decides by instinct, and that doesn't scale
Take any tire in the fleet. You measure tread depth at 4 points across the tread: outer edge, outer center, inner center, inner edge. An experienced mechanic looks at those 4 numbers and knows instantly: "this tire is worn because it's misaligned" or "this one's a pressure problem." Those are two different physical problems with two different fixes (align vs. adjust pressure), and the pattern across the 4 points is what gives it away:
- Wear concentrated on one edge (one edge with noticeably lower tread than the other) → the tire is "eating" on one side, a classic sign of a camber/alignment issue → needs alignment.
- Wear concentrated in the center OR on both edges, symmetrically → classic sign of wrong pressure (an underinflated tire wears the edges faster; an overinflated tire wears the center faster) → needs a pressure adjustment.
- Both patterns at once, or neither clearly → irregular wear, probably a different root cause (bearing, suspension) that this simple diagnosis doesn't cover.
The interesting part of modeling this in code isn't the math (it's subtraction and comparing averages) — it's deciding where the tolerance thresholds sit and what to do when both patterns show up at once. A tire with a 1mm difference between edges isn't misaligned, that's normal manufacturing/wear variation. I set 3mm as the alignment threshold and 2mm as the pressure threshold — not magic numbers, they're the point where a real mechanic would stop calling it "normal" and start calling it "a problem." And when both thresholds trip at the same time, the right move isn't to arbitrarily pick one — it's to admit the pattern is mixed (DESGASTE_IRREGULAR / irregular wear) and flag it for manual inspection, instead of giving a falsely confident diagnosis.
def diagnose_wheel(reading):
delta_bordas = reading.borda_externa - reading.borda_interna
delta_centro_bordas = reading.media_centro - reading.media_bordas
desalinhado = abs(delta_bordas) >= 3.0
pressao_errada = abs(delta_centro_bordas) >= 2.0
if desalinhado and pressao_errada:
return WheelVerdict.DESGASTE_IRREGULAR
if desalinhado:
return ALINHAR_ESQUERDA if delta_bordas < 0 else ALINHAR_DIREITA
if pressao_errada:
return CALIBRAR_MAIS if delta_centro_bordas > 0 else CALIBRAR_MENOS
return OK
Seven tests cover all 6 possible verdicts, including the mixed case. The point isn't the code's complexity (it's low, on purpose) — it's that the business rule became something testable and reproducible, instead of "ask your experienced mechanic."
The second problem: not every maintenance category weighs the same
Solving the per-wheel diagnosis doesn't solve the real, fleet-wide problem: with 20 vehicles, each generating data across 5 different categories (tire pressure, tread, alignment, oil/filters, lubrication), whoever decides "I'm checking this vehicle today" has to compare things that aren't obviously comparable. Is a vehicle with tire pressure 20% off ideal more urgent than one with lubrication 20% overdue? Yes — and the reason isn't arbitrary: wrong tire pressure degrades the tire fast and affects braking/stability within days, while a lubrication delay has weeks of slack before it becomes a real problem.
That means the score can't be a simple average across categories. I modeled it as a weighted sum, where the weight reflects how fast ignoring that category turns into an expensive problem:
CATEGORY_WEIGHTS = {
"calibragem": 5, # tire pressure
"sulco": 4, # tread depth
"alinhamento": 3, # alignment
"oleo_filtros": 2, # oil/filters
"lubrificacao": 1, # lubrication
}
score = Σ (category weight × severity 0.0–1.0 in that category)
Each category's severity (0.0 to 1.0) can come from whatever criterion makes sense for it — days overdue, deviation from ideal range, whatever fits — the priority engine doesn't need to know where the number came from, only that it's already normalized. That separation (whoever computes severity ≠ whoever ranks by priority) is what lets you swap the criterion for one category without touching the ranking engine.
The actual result
Running against a fictional 7-vehicle fleet with deliberately messy data (each vehicle has a different problem):
TOP 5 — vehicles needing attention now:
1. VEHICLE-07 — score 15.0 (worst category: tire pressure)
2. VEHICLE-03 — score 6.4 (worst category: tread depth)
3. VEHICLE-01 — score 6.4 (worst category: tire pressure)
4. VEHICLE-05 — score 6.0 (worst category: tire pressure)
5. VEHICLE-02 — score 1.9 (worst category: tread depth)
This is the real output of the repository's example CLI, not an illustration. A list where "everyone needs something" turned into an ordered list with an explicit reason attached.
Why this matters for a real operation
The win isn't "having a nice dashboard" — it's taking a recurring, expensive decision (which vehicle do I check first, every day) out of one person's head and putting it into an explicit, auditable, consistent criterion. That matters for three concrete reasons:
- The decision stops being a single point of failure. If the mechanic who "knows it by heart" gets sick, goes on vacation, or changes jobs, the operation doesn't lose its ability to prioritize — the criterion lives in the system, not just in someone's head.
- The criterion becomes visible and debatable. When each category's weight is explicit in the code, you can question and adjust it ("should alignment weigh more than this for our specific operation?") instead of arguing about a decision nobody can explain the reasoning for.
- Fewer unplanned breakdowns. The end goal isn't the score — it's the tire that doesn't blow out on the road because someone looked at it three days before, not three days after.
Trade-offs and limitations (on purpose, not hidden)
No system modeled in a few weeks is complete, and I'd rather say so than have someone find out the hard way:
- The thresholds (3mm, 2mm) and weights (5,4,3,2,1) are fixed constants, calibrated from my domain understanding, not from statistical analysis of a large base of real failures. For a different operation (different vehicle type, climate, road conditions), those numbers likely need adjusting.
- The wheel diagnosis covers 2 wear axes (edge-to-edge and center-vs-edges). A wear pattern that doesn't fit either one (say, caused by a bearing or suspension issue) falls into "irregular" without pointing at a root cause — the system can say "something's wrong," not always "what."
- The priority score doesn't factor in cost, part/shop availability, or route criticality. It's an inspection-severity score — one piece of a larger decision system, not the final decision by itself. A vehicle can have the highest score and still not be the truly most urgent one if the part to fix it won't arrive until tomorrow regardless.
- It trusts the input reading as true — if the tread/pressure data going in is wrong (measurement error, typo), the system propagates that error with the same confidence as correct data. That's exactly why I treat data extraction (reading a handwritten card via AI, in a different project in the same portfolio) as a separate layer, with its own validation, before anything reaches this one.
Why I rewrote this from scratch in Python instead of publishing the original system
The production version of this system isn't Python — it's native Google Sheets formulas and Google Apps Script, running inside a real client spreadsheet. That's a deliberate choice, not a limitation: when the person operating the system is a mechanic or a fleet manager, not a programmer, putting the logic directly into the tool they already use every day (the spreadsheet) removes an entire layer of friction — no deployment, no server, no "call IT to change it." My job as an engineer here wasn't "write as much code as possible," it was understanding the maintenance process closely enough to know WHICH rule to automate and WHERE it needed to live to actually get used.
Spreadsheet formulas tied to one client's specific spreadsheet structure aren't something I can publish, though (it's the client's, not mine to distribute), nor are they easy to read for anyone who can't open that exact spreadsheet. For the public repository, I rewrote the same decision logic from scratch in plain Python, tested in isolation, and generalized enough for any fleet operation to understand and adapt — with no company name, plate number, or any identifiable data from the original operation. The Python here is a portfolio translation, not the tool I'd reach for to solve this again from scratch in a real operation.
Repository: github.com/siandrosena/fleet-maintenance-priority-engine — Python, 14 tests, MIT license.
Siandro Sena — Production Engineer (background in Materials Engineering), MBA in Artificial Intelligence. Process automation with AI, data, and operational efficiency. LinkedIn
Top comments (0)