Ry Keel
Incorrect password
Product Design · Fleetio
Tires are one of the biggest recurring costs in any fleet's operating budget. Pressure, tread depth, position on the vehicle, install history, mileage. The kind of structured data a maintenance platform should be able to hold.
For years, Fleetio had no way to hold any of it. No tire records, no position tracking, no history. If a customer wanted to manage tires in our product, there was nothing to manage them with. So they improvised. Some built custom inspection items. Some used free-text notes. Some, like the example below, dropped raw readings into the comments section of a vehicle page.
"08/27/2022: LF 5/32 LR 7/32 RF 6/32 RR 7/32." A real comment from a real customer, sitting on a vehicle page for two years, holding maintenance-critical data in a field that was never designed for it.
This pattern was everywhere, and a few things lined up at once. Sales had been telling us for years that tire management was a frequent ask from prospects, and often the thing that decided whether a trial converted or churned. Existing customers were vocal about it too. The opportunity was real on both sides: new revenue from deals we'd been losing, and retention from customers who needed this to do their jobs.
That's what made it a prime candidate for the roadmap. We committed to shipping it in a single quarter, but before any of that work could start, we had a lot to figure out. What exactly were customers asking for? What were we actually building? How did this industry work, and how did tires fit into it? None of that was obvious from the backlog, and I couldn't design with confidence without it.
Where to Even Start
We had a backlog full of feedback going back years, and the PM had done solid discovery before I came onto the project. Between the two, we had a good understanding of the basic things users would need. The foundation was there. What we didn't have was specificity, and specificity is the difference between something that just works and something that's actually good. Which tire model attributes mattered most? How to represent the variability of axle configurations across vehicle types? Where in the product the feature should even live?
Before I could design anything real, I had to become something of an expert on tires.
I got a little obsessed during this stretch. I'd be driving to dinner with my wife and start pointing out axle configurations on trucks we passed. She tired of that fairly quickly. But the immersion mattered. We were designing a digital product to manage a physical one, and you can't do that well without understanding the physical one first. What do the numbers on a tire sidewall mean? Why does position on the vehicle matter? How do drivetrains differ across axle layouts?
This is a fraction of what ended up on my screen during discovery. Sidewall conventions, drivetrain components, axle layout taxonomies, vehicle classifications. The product had to be legible to fleet operators who knew this material cold, which meant I had to learn enough of it to design with confidence instead of guessing at conventions.
One pattern stood out across all the reference material I was pulling: nearly every axle diagram I could find was static. They were drawn by graphic designers for technical manuals or training materials, meant to be viewed or printed, not touched. A truck rendered from above with tires hanging off the axles. Good at communicating what a vehicle is. Useless for actually doing anything with it.
That became the design problem. How do you take a visual language fleet operators already recognize and turn it into something they can interact with? Something that holds real data, surfaces it per position, and supports the workflows they actually run on it.
The left side is closer to the traditional view: a top-down representation, accurate enough but inert. The right side is where we landed. Same bird's-eye orientation fleet managers already had a mental model for, but every tire is now a record. Tread depth and PSI surface per position. Axle types are editable. The diagram became the interface.
Talking to Customers
Once I had a lightweight working version of the interactive axle layout, something concrete enough to react to, I started bringing it into customer calls. The goal wasn't validation in the marketing sense. It was pressure-testing whether the choices I'd made matched how operators actually thought about their vehicles.
About 20 customers participated, which is a lot for a single quarter. Two sources got us there fast. The PM had been in the industry a while and had a handful of long-standing customer relationships we could lean on as partners. For the rest, I shipped an in-app survey asking whether people would be interested in a tire management feature and, if so, whether they'd be open to an interview. A Calendly link did the rest. That filled the roster in days.
I ran the sessions mostly solo. The PM joined a few when he wanted to, but most of the time it was just me and the customer talking through how they actually managed tires today. The format wasn't precious. I'd put a version of the prototype in front of them, ask what they expected to be able to do with it, and watch where reality diverged from my assumptions.
The whole thing was running against the clock. Research, design, and development were all happening inside the same quarter, which meant I was often jumping out of one interview and straight back into Figma, tweaking the prototype, sharing the change with engineering, and bringing the new version into the next call. The cadence was fast on purpose. Every conversation had to pull double duty: validating what I'd just changed and surfacing whatever was about to change next.
One interview in particular pulled the design in a direction I hadn't accounted for. A customer pointed out that most tires being added to the system weren't going to be new. Some would be used tires with mileage already on them, some would be retreads, and many would be tires that had been physically installed on vehicles months before we shipped the feature. The data was going to arrive in catch-up mode. That meant the install workflow couldn't assume a clean starting point. We needed to let users specify a backdated install date, capture starting mileage when a tire wasn't fresh, and intelligently figure out current tire mileage based on how much the vehicle had been driven since the install date.
The position detail view ended up doing a lot of quiet work here. Install Date and Tire Meter sit right next to each other intentionally. Together they let a user say "this tire was installed three months ago and already had 5,000 miles on it then," and the system handles the rest, layering the vehicle's accumulated mileage on top to keep the tire's lifetime usage accurate without anyone having to do the math.
I walked away from that call with a clear sense that the data model needed to flex around real-world install scenarios, not the idealized version I'd been designing toward. That shaped a lot of what came next.
The Foundation We Shipped
The V1 MVP went out as a closed beta in spring 2024. The data model was built around three primitives: a configurable axle layout per vehicle, tires as first-class records, and a small set of lifecycle activities (install, rotate, remove) that captured what happened to a tire over time.
The goal at this stage wasn't breadth, it was correctness. I wanted the data model solid enough that anything we built on top of it later wouldn't require painful migrations. I made the deliberate call to ship with a fixed list of axle configurations, each paired with a visual diagram. Full customization would have consumed the quarter, and I didn't yet have the customer signal to design it well. Shipping with constraints and letting real usage tell us where the ceiling needed to move was the better tradeoff.
The Decision That Mattered Most
A few months into beta, the pattern was clear. The standalone tire screens, on their own, weren't going to drive adoption. The screens worked. They just asked users to come to a new surface to do a job they were already half-doing somewhere else.
The thread I kept pulling on from the early interviews was that customers had already built their own workaround. They were adding custom inspection items to their daily inspection forms to capture tire pressure and tread depth. The data existed. It had nowhere to go. No tire record it could feed, no history it could build, no alert it could trigger.
For the GA release in September 2024, I reframed the problem. Instead of asking users to come to tire management, I made tire management come to them. We shipped a dedicated Tire Readings inspection item with structured fields for tread depth and pressure, embedded directly into the inspection flow operators were already running every day.
Under the hood, this was the first inspection item we'd ever built that adapted to the vehicle being inspected. The admin sets it up once with a minimum tread depth and pressure threshold. From there, the inspection item handles itself. When a driver opens the inspection on a 4x2, they see four positions. Open it on a 6x4, they see ten. An 8x8 fills out accordingly. The admin never has to define which positions exist on which vehicles, and the driver never has to skip fields that don't apply. The item just reflects whatever axle configuration the vehicle is set to. It felt a little magical the first time we saw it working end-to-end, and that adaptivity ended up being one of the things customers called out most often once it shipped.
The standalone tire management screens stayed in place. Users could still go to a vehicle's tire management tab to manually update tread depth, pressure, and tire details whenever they needed to. The inspection item didn't replace that. It became a passive way to feed the same data in through a loop users already had muscle memory for.
This is the decision I'm most proud of on the project. The feature stopped being a parallel workflow and started feeding an existing one. The Tire Readings item became one of the most heavily used elements of the entire module within weeks of GA.
Another Path to Automation
If GA taught us anything, it was that the real customer need underneath all of this was automation. People didn't want to type tire data into a system. They wanted the system to already know. The inspection item was one answer to that. We started looking for others.
In April 2025, we integrated with a third-party telematics provider to pull tire pressure readings directly from sensor-equipped vehicles. Where the inspection item collected data from a driver doing a walkaround, the telematics integration collected it continuously from sensors on the vehicle itself. Two completely different paths into the same tire management module.
The list view above is where the two paths converge. Readings show up regardless of where they came from. Manual entries from a fleet manager, structured entries from an inspection, automated entries from telematics. All filterable, all grouped by health status, all feeding the same tire records.
Looking back, the thing I'd flag for myself on a future project is to treat customer requests for "automation" as a category of problem rather than a single feature. There's almost always more than one path worth pursuing, and the paths don't have to depend on each other to add up to something bigger than either one alone.
Where It Stands
Tire Management didn't land on a single launch. It landed because we treated it as a deliberate sequence of waves, each scoped against the most pressing gap surfaced by the one before. V1 in spring 2024 to prove the data model. GA in September 2024 to meet users where they already were. Q1 2025 to sand the rough edges. Automated readings in April 2025 to make the everyday task lighter still. Each release gave the next one something to build on.
That cadence kept the design improving in public, kept our customer-facing teams armed with something new every quarter, and kept me close to real usage instead of my own assumptions. It's the part of the project I'd carry into anything I worked on next.
Lessons Learned
No project ships clean, and this one had its share of decisions I'd revisit if I were starting over. A few worth calling out.
The constrained axle model. I knew we'd hear from customers running configurations we hadn't included in V1. We did. What I underestimated was how much more expensive it would be to broaden the model later, once real customer data had accumulated against the original schema. If I were starting over, I'd push harder on flexibility in the data model from day one, even at the cost of slower delivery.
Underestimating the need for customization. Related but distinct. We went into this thinking that once we'd done enough research, we'd have a definitive list of axle configurations that would cover the fleet world. That confidence didn't survive contact with reality. Vehicles have more variability than any preset list can capture, and every few weeks someone from CS would surface a new example we hadn't accounted for. The lesson isn't that we picked the wrong configurations. It's that for something this varied, no preset list is ever going to be enough. A customization layer should have been part of the foundation, not a thing we deferred.
Missing the technician workflow. We did well at automating the data paths into tire management for drivers (via inspections) and for vehicles themselves (via telematics). The path we missed was the technician. When a truck goes into the shop for tire replacement after a blowout or an end-of-life swap, someone is physically doing that work, usually inside a work order. That's the moment when the most accurate, structured tire data of the entire lifecycle gets generated, and we weren't capturing it. For larger customers with in-house shops, this was a real gap, and one I wish we'd prioritized from the start.
Product Design · Fleetio
Every service invoice tells a story. A vehicle came in, work was completed, parts and labor were charged, and someone eventually had to turn that paper trail into a service entry.
For a lot of fleets, that last step was still completely manual. An invoice would show up in an inbox, get passed across a counter, sit in a stack on someone's desk, or get forwarded from a driver's phone. Then a fleet manager or office admin would open Fleetio, create a service entry, type in the vendor, vehicle, completion date, reference number, odometer, cost summary, service tasks, parts, labor, and notes. The same information already existed on the invoice. Fleetio just did not know how to read it.
Smart Upload started as a simple idea: upload an invoice, let AI extract the details, and create a draft service entry for the user to review.
That was the easy version of the problem. The real version was messier. Invoices arrived through different channels. Customers wanted to process more than one at a time. Some needed lightweight totals, while others needed detailed parts and labor breakdowns by service task. Smart Upload was helpful, but it was not always right. And when it was wrong, the product needed to help users understand what happened and recover without losing trust.
That became the actual design challenge. Not just making invoice upload possible, but making a Smart Upload workflow feel reviewable, scalable, and safe enough for fleet teams to use every week.
The Paper Stack Problem
The clearest customer feedback came from one of our early beta users. Their team was trying Smart Upload, but the one-at-a-time experience was not solving the real problem. They were still receiving stacks of invoices and had one person responsible for getting through them.
This is an actual photo sent by a customer, showing what their desk looked like before Smart Upload.
The line that stuck with me was that the paper stack just kept increasing. That was a useful gut check. A technically impressive upload modal did not matter if it still forced someone to babysit each invoice one by one.
Around the same time, I ran a short survey to understand how fleets actually handled invoices. The results were clarifying. Fleet managers and office admins were the people usually receiving invoices and creating service entries. Drivers were involved far less often. Invoices mostly arrived by email from shops or through physical handoff.
That immediately shifted how I thought about the product. This was not primarily a driver workflow where someone should snap a photo and create a final service entry from the field. Drivers could still be a useful capture path later, especially for paper invoices, but the core workflow belonged to the fleet manager or office admin sitting down to review and approve a backlog.
The product needed to support that reality. Uploading one document from a form was not enough. We needed a queue. We needed background processing. And we needed to separate capture from review so the person submitting an invoice did not have to be the same person finalizing the service entry.
The data eventually confirmed what the survey and the paper stack story had been telling us. Roughly sixteen percent of the invoices customers eventually uploaded were more than six months old. Almost half of those were over two years old.
That long tail was the paper stack made visible. Customers were not just keeping up with new invoices, they were finally working through years of records that had been sitting on a desk somewhere. Bulk processing was not a nice-to-have for a small slice of power users. It was how a meaningful share of customers actually used the feature, and it was the part of the experience that took an old source of anxiety off their plate.
Designing for Review, Not Magic
The first version of Smart Upload lived close to the service entry form. A user could upload a document, wait while it processed, review the extracted fields, and create the service entry.
That worked as a proof of concept, but the design could not treat Smart Upload like it was magically correct. Fleetio service entries are operational and financial records. If Smart Upload put mileage in the wrong field, missed a vendor, chose the wrong vehicle, or mapped cost to the wrong service task, the user still owned the final record.
So the review experience became the center of the product.
I wanted the interaction model to stay familiar. The right side looked and behaved like Fleetio's existing service entry form patterns, just condensed into a review surface. The user could correct extracted values, fill required fields, discard the upload, or create the service entry. The invoice stayed visible beside the fields so they could verify without jumping between tabs.
The small design details mattered here. Processing needed to feel active, not broken. Dropdowns needed to stay visible inside the modal. The primary action needed to be disabled until required fields were resolved. After creating a service entry, the toast needed a clear path to view what was just created, because otherwise the modal simply advanced to the next upload and users could lose track of where their work went.
This was the first major lesson of the project: with Smart Upload, the review state is the product. The upload moment gets attention, but trust is built in the correction, confirmation, and recovery moments.
One Invoice Was Not Enough
The more customers used the early versions, the more obvious it became that single-file processing was only a starting point. The highest-value customers did not have one invoice. They had batches.
I started exploring a lightweight Smart Upload queue that could let users upload multiple invoices, keep working while the files processed, and come back later to review each one. At first, I imagined a fuller index experience with filters, sorting, reviewed states, and richer metadata. That was the eventual shape of the feature, but it was too much for the next release.
The near-term question was more practical: what is the smallest queue we can ship that still changes the workflow?
The answer was a modal-based queue. Files appeared in a list on the left. The selected upload opened in the review panel on the right. Processing files used row-level status indicators so users could tell what was still working and what was ready. New uploads could be added from inside the modal. Once an entry was created, the next upload moved into focus.
That structure let us support the behavior customers were asking for without forcing a larger architectural shift before we had enough signal. It also kept the review moment focused. Users were still doing one final review at a time, but they no longer had to upload and wait one invoice at a time.
There was a lot of product judgment in that tradeoff. A full Smart Upload index would have been cleaner in the long run, but slower. The modal queue gave us enough structure to support batch processing and learn from real usage before investing in a larger surface.
The Detail Hiding in the Totals
Once batch processing was in motion, the next problem became cost detail.
A service entry is not just a total. It can include multiple service tasks, each with its own parts cost, labor cost, subtotal, reason for repair, and maintenance categorization. Many invoices include all of that information, but the early Smart Upload experience was mostly focused on getting the high-level fields and cost summary right.
That was not enough for customers who used service entries as a detailed maintenance record.
I explored a few ways to make the AI's cost allocation visible. One version used tooltips on parts and labor fields to explain how the model arrived at each amount. Another used an always-visible notes area. Another treated extracted invoice line items as removable chips or grouped values that could be assigned to parts or labor.
The design problem was not just how to show more data. It was how to show enough reasoning for users to trust or correct the output without turning the review panel into a spreadsheet.
Where we landed for the next release was more grounded. Service task cards would include editable parts and labor fields, a read-only subtotal, and supporting text showing the invoice line items the AI had associated with each task. If the AI found costs it could not confidently match to a service task, the UI would warn the user that the service entry total differed from the invoice total.
This was one of the more important design decisions in the project. I originally considered a button that said something like "Fix unmatched line items," but the wording implied that clicking the button would automatically solve the problem. It would not. It would only move the user to the place where they could resolve it themselves.
So I moved away from the button and toward clearer warning language. The goal was to make the issue visible without pretending the system could fix something it could not fix yet.
Planning the Release Path
Smart Upload was moving quickly, and the product was changing while people were already using it. That made version planning unusually important.
I created and maintained a release plan inside the product brief so the team could see how the feature was evolving across alpha, limited releases, beta, EAP, and later general availability. Each version documented the target audience, what changed from the previous version, what we were trying to learn, what limitations remained, and what feedback should influence the next release.
That planning work became more important than I expected. It helped the team avoid treating every idea as part of the next release. A full index mattered, but the modal queue was enough to learn from. Advanced line item processing mattered, but it needed to follow batch upload so customers had a reason to process more invoices through the system.
It also helped other teams understand what was happening. Product marketing needed to know what could be communicated. Customer-facing teams needed to know what was coming and what was still limited. Engineering needed clearer slices of work. QA needed to know which behaviors belonged to which release.
This is one of the parts of the project I am proud of that does not show up in a screenshot. The feature needed design artifacts, but it also needed a shared map.
How It Landed
Smart Upload for Service Entries did not become useful because one model extraction got better. It became useful because the workflow around the model became more realistic. The feature moved from a single upload experience to a reviewable queue. It gained batch processing, clearer processing states, better feedback paths, and more detailed line item cost handling. We started treating AI output as something users review and shape, not something the product should blindly trust.
The clearest signal that the shift was working came from what happened after GA. Customers kept using it. Then they used it more.
Weekly upload volume has roughly tripled since GA. That curve does a lot of work because it answers the question I would have asked watching this project from the outside: did people actually come back to it? The answer was yes, and the rate of return kept growing.
What I cared about more than raw volume was whether users finished what they started. Smart Upload can generate a lot of uploads without generating a lot of completed work. The completion rate became the truer measure of whether the review experience was actually working.
Eighty-five percent of uploads ended in a created service entry. The other fifteen got discarded or abandoned somewhere in review. That ratio is the one I point to when I talk about this project, because it is the closest thing to a verdict on the review experience itself. If the modal had been too noisy, the warnings too vague, or the corrections too painful, that number would have been much lower.
The other number I tracked closely was how many accounts were coming back week over week, not just uploading once and moving on, but returning to the feature as part of their regular workflow.
That curve starts in the EAP period and runs through GA. The shape of it is the point: not a spike followed by drop-off, but a slow and steady climb that kept going. Accounts were not just trying Smart Upload once out of curiosity. They were building it into their week.
The last piece of the story was outside the product entirely. Smart Upload started showing up in sales conversations.
The sales team began tagging won deals where Smart Upload had been a factor in the customer's decision to choose Fleetio. The count and the deal value both climbed steadily through 2026. That data was not something I built into the case for the feature when we scoped it, but it became one of the more useful pieces of evidence that the work was paying off beyond product metrics.
These numbers do not belong to me alone. The product team, the sales team, customer success, marketing, and engineering all shaped how the feature landed in market. But the design choices around review, batching, and line item handling are the ones I can defend, and the curves above are the ones that suggest those choices were the right ones to make.
The shift also opened the door for Smart Upload to expand beyond service entries. The same pattern started being explored for inspection forms, fuel entries, and other document-heavy workflows. The service entry work became the foundation, not the endpoint. The part I would carry forward is the disposition behind that expansion: AI features do not need to be perfect to be valuable, but they do need to be honest. The interface has to show what it knows, make uncertainty visible, and give users a path to fix what the system cannot.
Lessons Learned
No AI project ships clean, and this one had plenty of places I would approach differently if I were starting over.
The queue should have been the center earlier. We started with the upload and review moment because that was the smallest proof of concept. That made sense technically, but the customer workflow was always bigger than one invoice. The paper stack was the problem. If I were starting over, I would frame the first design around a queue from the beginning, even if the first implementation still processed one file at a time.
Confidence scores were less useful than behavior. Early on, I wanted a way to show users when the AI was confident or uncertain. That sounds right in theory. In practice, provider-generated confidence scores were not reliable enough to trust. A better path is to build confidence from user behavior: what fields users change, what they leave alone, where totals mismatch, and where they abandon or discard uploads. That kind of confidence is harder to build, but much more meaningful.
Line item costs were the real complexity. Extracting a total from an invoice is useful. Turning a messy invoice into structured service task, parts, labor, and subtotal data is much harder, and much closer to what power users actually need. I would have pulled that complexity forward sooner in discovery, especially with customers who rely on detailed service histories for reporting and maintenance analysis.
Trust is mostly built after the AI is done. The processing animation, the upload moment, the "smart" branding, all of that helps. But trust is really built when the user sees the result. Did the vehicle match? Did the vendor match? Did the total tie out? If something failed, did the product explain what happened? The more I worked on Smart Upload, the more I came to see the review experience as the real product surface, not just a required step before saving.
Senior Product Designer · Based in Knoxville, TN
Before product design, I spent years in the classroom teaching high school honors world history. I made the switch to UX, and eventually product design, in 2019, and it turns out the skills aren't that different. The work is still fundamentally about understanding how people think, where they get stuck, and how to meet them where they are.
Outside of work you'll find me on a bike, on a rock face, behind a camera, or making something unnecessarily complicated on a sewing machine. I also love video games, which probably explains the aesthetic choices around here.