Project retrospective
A Year Building UMA Companion: Why I Sunset the Project
What began as a support-card manager became a desktop app, an optimizer, a data pipeline, and a live game companion before I decided that continuing to maintain it no longer made sense.
UMA Companion is now a completed and sunset project. The archived UMA Companion website remains available as a record of what I built, while this post explains the engineering story behind it.
I started UMA Companion because Uma Musume: Pretty Derby makes a surprisingly large amount of game knowledge difficult to manage. Support cards have different effects, levels, skills, events, and training roles. A useful deck depends on the scenario, the race goal, the cards I actually own, and the card I can borrow. I wanted something better than keeping the answer in my head or opening several disconnected reference pages.
Over roughly a year, that small idea grew into a substantial software project. It reached users and testers, accumulated feedback, and changed shape several times. The important part of the story is not only that development ended. It is that the project became large enough for the decision to stop to be an engineering and product decision rather than a loss of interest.
The problem I was trying to solve
The original goal was straightforward: keep track of support cards and use that information to make better training decisions. The first useful version needed a card catalogue, a personal collection, saved decks, and enough game data to make recommendations meaningful.
That scope immediately exposed the real problem. Displaying cards is easy; deciding which six-card deck best fits a particular set of constraints is not. The project had to represent owned and borrowed cards, support-card characteristics, skills, scenarios, races, tracks, and the different ways those pieces affect a run.
V1: learning the shape of the problem
V1 was a Python desktop application. It began with CustomTkinter, then moved to PySide6 when the UI needed better rendering, styling, and interactions such as drag-and-drop deck building. That transition was a useful lesson in itself: the first implementation had solved enough of the problem to reveal where its foundations were becoming restrictive.
I built an AETHER-style design system for the PySide6 version, kept the application local-first, and used SQLite for the catalogue and user-owned data. V1 was not embarrassing legacy code. It was the working experiment that proved the project was useful and showed me which boundaries needed to become more deliberate.
V2: a serious rewrite
V2 was not simply a visual redesign. The project moved toward a Rust core and a TypeScript/TSX interface, with a more structured separation between application code, catalogue data, and user data. The rewrite was driven by the growing needs of the software: a richer desktop experience, a reusable data model, more predictable distribution, and an architecture that could support the optimizer and live companion features without turning every change into a patch on the original prototype.
The result was a desktop tool that shared one local collection across its views. A card updated in the collection was available to deck planning, database browsing, and other tools. The project also expanded into a web platform so players could access parts of the catalogue, optimizer, and community features without installing the Windows application.
The optimizer was the hard part
The optimizer was the feature that made UMA Companion more than a database browser. A legal deck has six support-card slots, an owned collection, a possible borrowed or friend slot, scenario-specific requirements, and a large set of interacting effects and skills. The search space grows quickly, and a result that looks plausible is not automatically a result that is correct.
I used beam search to evaluate many possible six-card combinations against the selected requirements. The useful engineering challenge was not merely generating permutations; it was deciding how to score them, how to represent constraints, how to keep the result explainable, and how to validate that a promising deck was not just an artefact of a flawed scoring rule.
That distinction still matters when looking back at the project. Official game tooling becoming better did not replace the optimizer. The optimizer remained a distinct piece of work. The question became whether keeping an entire surrounding application ecosystem maintained was still justified for the set of features that remained differentiated.
OCR and Live Run
Later work moved the project closer to the game itself. Live capture and OCR were intended to bring useful game state into the companion workflow rather than asking the player to type every value into a form. That meant dealing with screen regions, imperfect recognition, changing layouts, and the difference between text that is easy for a human to read and text that is reliable enough for software to act on.
Live Run and training assistance made the engineering problem more interesting and more fragile at the same time. A normal database query has structured input. A screenshot does not. It can contain scaling differences, visual noise, partial state, and a recognition error that only becomes obvious several steps later. Working on that boundary taught me to treat computer vision output as uncertain data that needs validation, not as a clean API response.
Data, synchronization, and releases
A live-service game also turns reference data into a maintenance system. UMA Companion grew to include support cards, skills, races, tracks, events, scenarios, and the relationships between them. The catalogue needed to change as the game changed, while the user's collection, decks, notes, and wishlist needed to survive those updates.
One early failure made that boundary clear: replacing the SQLite database could wipe a user's local save data. The eventual approach separated the executable from a catalogue data pack and merged new catalogue tables while preserving user tables. Scraping and data preparation moved to the maintainer side, with Playwright-based jobs and GitHub Actions producing lightweight data packs for users instead of shipping a browser automation stack inside the application.
By the end, UMA Companion was not just an executable. It had a website, downloadable releases, update mechanisms, a data pipeline, a web platform, and multiple application surfaces that all depended on a changing game. The project had become a small ecosystem.
Why I sunset UMA Companion
During 2026, the Japanese version of Uma Musume began receiving and announcing more official guidance and information functionality, including the Trainer Guide. The relevant announcements belong to the game's official Japanese news archive, rather than community speculation.
This did not make UMA Companion useless, and Cygames did not replace everything it did. The optimizer in particular remained a different and more advanced kind of tool. But the overall value proposition changed. Some of the friction that originally justified an external companion was increasingly being addressed inside the official game experience.
At the same time, maintaining UMA Companion meant keeping the desktop frontend, Rust backend, optimizer, OCR and screen capture, game database, synchronization logic, scraping and data pipeline, website, releases, and update infrastructure compatible with a live-service game. The project was no longer a small utility that could be refreshed occasionally. It required a meaningful amount of work to preserve a shrinking set of differentiating advantages.
Eventually the product question had a clear answer: did the additional value still justify maintaining the whole system? For me, the answer became no. Sunset was not an admission that the project failed. It was the decision to stop spending maintenance effort simply because I had already spent a year building it.
What the project taught me
Architecture is discovered through use
The path from a Python prototype to the later Rust and TypeScript application was not a clean master plan. Each version exposed a new boundary: UI performance, local data ownership, reusable optimization logic, distribution, and the difference between catalog updates and user state. A rewrite can be worthwhile when it expresses those lessons rather than pretending the first version never happened.
Algorithms need evidence
An optimizer can produce a convincing answer while still being wrong. Building one forced me to think about constraints, scoring, domain assumptions, and validation. “It looks like a good deck” is not enough when the feature's purpose is to make decisions more trustworthy.
Messy inputs change the design
OCR and screen capture made the cost of uncertainty visible. External data sources and changing game content did the same thing from another direction. Both pushed me toward explicit synchronization, safer data boundaries, and treating updates as part of the product rather than an afterthought.
Finishing is also a product decision
Software does not need to run forever to be successful. UMA Companion solved a real problem, grew through major architectural changes, reached users and testers, and gave me difficult engineering problems to work through. Choosing to sunset it when the maintenance cost no longer matched the value is part of the project's outcome, not something I need to hide.
UMA Companion is preserved as an engineering case study and archive. You can view the archived project website, including its catalogue, optimizer, releases, and surviving application documentation.