Overview
EDITED is a market intelligence platform used by some of the world’s largest retailers and brands to track competitor assortments, pricing and promotions. When I joined as Senior Product Designer in 2020, the company had a Machine Learning (ML) product-matching model in development and a feature called Saved items that was supposed to expose it. Saved items had a bad reputation, and customers weren’t using it. Lists was the rebuild. It shipped in April 2021 as a spreadsheet-like working surface where customers could pull together small comparison sets of products and run the ML model against them. It was the first of three product-matching tools I designed at EDITED, followed by Product Matching v1 and EDITED Match, each written up as its own case study. This one is about Lists on its own terms.
Note: This case study describes work completed at EDITED. Client names and commercial details have been generalised to respect confidentiality, and any figures shown are illustrative rather than sourced from EDITED.
The challenge
Saved items had roughly 1% usage among existing customers. That number was the quantitative shape of the feature’s bad reputation, and it was what I was handed on day one. The flow was three steps. You added products to a side-panel clipboard as you browsed, one at a time. You turned the clipboard into a board when you were ready. You opened the board to see your selection laid out as a table. And then the table did nothing. You couldn’t sort it, reorder columns, add more products, or do any of the comparison work customers had been building the board to do. So they exported to Excel and did it there.
The model the company had been building in parallel, an ML system that could match the same or similar products across retailers and regions, could now produce results but couldn’t yet be trusted to run unsupervised. That was the gap Lists had to close. We needed a working surface that let customers bring their own comparison logic to the table and use the model as one action within it, not a UI that pretended the model was more finished than it was.
The team was small. One PM, who was early in his career and on his first big feature. An engineering team implementing against AG Grid. The data science team working on the underlying model alongside us. No one expected this to be the last word in product matching at EDITED. We treated it as an MVP, with the explicit job of shipping something good enough for customers to use, so we could learn from real usage what to build next.
How I approached it
The reframe was small but load-bearing. Lists wasn’t a checkout, it was a working surface. Once I saw it that way, the rest of the project had a shape.
The first half was discovery. Sixteen customer interviews across three rounds, from luxury marketplaces to outerwear brands to sportswear retailers, turned into an affinity diagram grouped around the jobs customers were actually trying to do: view and customise tables of products, group them for comparison, match products across retailers, pull their own data in as custom rows and columns, and run simple calculations. The features we shipped map directly onto those themes. The ones we knew we’d need later (formulas, alerts, scheduled exports) got parked as ambitions.
Testing with internal colleagues before each build cycle
Replacing the basket, and framing the craft
The call that shaped everything else was the table library. We used AG Grid rather than build a spreadsheet-style component from scratch. That was a pragmatic decision for shipping speed, but it meant design had to live in a real push and pull with the library. Design too freely and the component wouldn’t support it. Design too tightly to it and Lists would feel like a generic data grid rather than a considered product surface. Most of the craft of this project lived in that gap.
The first thing the reframe killed was the clipboard-then-board flow. If Lists was a working surface, there was no reason to separate assembling a set from working on it. Customers already used EDITED’s market intelligence filters every day to find products, so Lists plugged straight into that habit: filter a view down to the products you cared about, then either add an individual product to a List or send the entire filtered search into a List in one move. Once inside, the same filters and the ML model were available as in-context actions. The commit step disappeared. You could keep adding, removing, grouping, matching and pinning without leaving the surface.
Running the rename as its own project
Grouping, and the stacked-table decision
The clearest example of the push and pull with AG Grid was grouping. A customer comparing a product across markets (say, this shoe in the UK, France and Germany) could see the matches as one unified table with collapsible group headers, or as a stack of smaller tables, one per market. I chose stacked tables, because a stacked layout let us place a per-table trigger at the bottom of each group that surfaced product-matching suggestions for that group in particular. A unified table couldn’t have supported that cleanly. The cost was real, mostly on the engineering side, where keeping data in sync across the separate tables took ongoing work. On EDITED Match, two years later, the trigger wasn’t needed and native grouping became the right call. That’s a different case study.
Pinning the baseline
The baseline product pattern was the other hinge. Every Lists search needed one reference product to anchor the comparison against, so all the price, availability and match metrics could be calculated relative to it. The pattern that landed was pinning a row as the baseline, with every metric in the table recalculated from that pin. It came out of an engineering planning conversation in June 2020, when we realised AG Grid’s pinned-row behaviour could carry the baseline semantics if we designed around it.
The detail I remember most fondly is the Group ID custom column. Customers kept asking to group by their own internal logic, their own trade strategy, their own tagging, none of which mapped to any column EDITED provided. Rather than build a full data integration (out of scope, out of budget), I added a column customers could populate with any identifier they liked and group by. A tiny extension to the library’s native grouping that unlocked a meaningfully more flexible comparison story without any new infrastructure.
Designing the matching mode
Matching was where language and state mattered most. The button settled on Find matches, paired with Close matches when the mode was active, after several iterations that read more like UI plumbing. The model could return up to a hundred candidates per baseline product, but the first ten to twenty were typically close to exact and the long tail degraded into loose similarity. Showing everything at once would have buried the good matches under the weak ones, so we paginated five at a time with the tightest matches first. A harder rule was what to do with the vs-baseline columns while candidates were still sitting in the suggestions panel. Showing the deltas on unadded candidates would have implied they were already part of the comparison. They weren’t. So matches only carry vs-baseline calculations once they’ve been added to the List. The suggestions panel is the candidate pool, the List is the committed set, and the visual distinction between them had to earn its keep.
Because a product existed in exactly one place at a time, adding a match physically moved it from the suggestions panel into the List. The move had to be legible. A disappear-and-renumber would have broken the mental model. The interaction I designed for was a short sequence: the Add-to-List button confirmed with a state change, the row lifted out of the suggestions panel, a new candidate slid into its place from the pool, the total count ticked down, and the new row appeared in the List above with the vs-baseline calculations now applied. About half a second end to end. Enough to feel deliberate, not enough to feel choreographed.
Scoping the match
The last piece was the retailer filter. Left alone, the ML model matched against every retailer in the catalogue, which was too wide for most customers. A luxury brand wanted a defined competitive set; a department store wanted to exclude its own sales channels. I prototyped the filter as a separate page, a side panel, and a modal. The filter held around five thousand retailers and needed a split layout (a selectable list on one side and a live summary of what was in scope on the other). At that complexity, a side panel would have felt cramped against the table it was supposed to serve, and a separate page broke the flow. The modal won on focus.
Base retailers as a global setting
Outcome & reflection
Lists shipped in April 2021, alongside the Product Matching Luxury Universe, as the first real customer-facing surface for the ML model. Customer response at release was the validation the MVP framing had been aiming for. A global luxury marketplace asked for immediate access. A premium outerwear brand asked to be in any beta we ran. For a feature that had been carrying 1% usage a few months earlier, the shift in posture was the clearest signal we’d got the direction right.
The more interesting outcome is what Lists taught the team. Customers used it, and in using it they showed us the shape of the next problem: the luxury customers trusted the matches and wanted to scale, the fast-fashion customers needed more human-in-the-loop review than Lists was built for, and the per-list scoping cap was going to be the bottleneck for the workflows that worked. The cap wasn’t a limitation of Lists, it was a property of the approach itself. Every List had to be anchored to a baseline product, and that framing couldn’t stretch to the larger, unanchored matching jobs the luxury customers were asking for. Product Matching v1 and EDITED Match got built as separate products rather than extensions of Lists for that reason.
The thing I carry forward from Lists is how much of designing for ML is about being honest about what the technology can do today. The temptation was always to skip ahead and build the proper product-matching tool. Lists worked because it didn’t. It gave customers a working surface they could get value from at the model’s current reliability, and it gave us real usage data to point the next iteration at. That’s the pattern: design for what the technology supports now, build toward what it will support later, and trust that the product will tell you when the frame has to change.