This is the behind-the-scenes view of Sportch v2.0 and how we reshaped UX subtly while bringing new game-changing features to fruition.
Team-first data model
Sportch v1.0 only catered for singles sports – one player vs. another player. The biggest goal for Sportch v2.0 was to break that mould by supporting teams. Social sports need captains, and doubles ladders.
The key question / challenge here was – how can we pivot to team support with minimal technical and UX impact? The key innovation and insight was to replace Player IDs with Team IDs. By givin each individual player on the system an individual team, we were able to simply remap all player IDs to team IDs. We refactored the code and migrated data so that teams are now first-class entities.
- Identifiers are coerced to team IDs end-to-end. appMatch, ladder feeds, profile match lists, and location feeds now resolve team IDs instead of assuming user IDs.
- Read-time normalization instead of risky migrations. Legacy key-value store rows missing sport_id or matchup-by data are repaired on read. Existing users reconnect and immediately land on a valid ladder without the need of a large-scale data migration.
- Team creation/edit UX. We added a new page for users to manage their teams. There are two ways for a user to manage their teams:
- Create multiple teams – each will get their own Sportch rating and ranking on whichever ladders that team enters.
- New teams will be given an automated team name based on the player names. The name can be changed, as can the roster (other than the team creator) and captain.
- Team chats occur only between team captains In order to minimise the impact of adding team support on the existing UX, we kept chats as one-to-one.
- Ratings and erosion respect squads. The rating engine now scopes activity to the team and sport, preventing cross-sport erosion and keeping multi-sport users honest.
Key takeaway: Treat “user vs team” as a type problem, not a UI problem. Make the data layer reject mixed IDs, and the impact on interface becomes predictable.
Server-driven ladders
Client-side filtering was implemented in Sportch v1.0 but, despite being very flexible, it was less explicit in its support for varying demographics, and in addition it did not – and could not – support teams. For teams to work we needed an explicit separation between ladder demographics – otherwise it would be possible to list both doubles teams and singles players in the same ladder, which makes no sense.
-
Server-side eligibility, filters, and pagination. The API returns the authoritative ladder slice plus paging tokens. Clients select the ladder and render it; they don’t re-filter.
-
Cons of the new ladder system One of the draw backs of this is that we’ve lost some of the filtering capabilities in the app – e.g. it’s no longer possible to see where you rank against players outside of the ladders which you have entered; something that version 1.0 did support. This facility is something that we will look at re-introducing at a later stage but at this stage was deemed a worthy sacrifice to reduce the impact in UX.
Key takeaway: When business rules (eligibility, matchup ranges, match types) get complicated, it’s more practical to make them explicit and move them server-side.
Chat UX upgrades
Chat UX is really about delivery guarantees and convenience. One of the drawbacks of v1.0 is that the chat facility was significantly less convenient than leading chat apps. The features that were lacking were:
- Message notifications were not fully featured – they only showed that a message had come in – not the message itself, nor who it was from.
- Clicking on the notification would just open the app, not the chat itself
- Unread message counts weren’t reliable
- Online status of other users wasn’t reliable
- Emojis were not available - preventing quick “thumbs up” replies
- Group chats were not available
- Chats were only able to be initiated as invitations to a match-up. Not just for general chat. A chat would only be started if the invitation was accepted.
- Score reporting was only available after a chat invitation was accepted. This made spontaneously entering a score after a match in Sportch was labourious and awkward with many steps to get it done.
We addressed most of these limitations, improving the chat UX significantly.
- Richer push payloads. Notifications now include author + snippet, when viewed.
- Deeplink resilience. Deeplinks to chats work whether the message originated on web or mobile. If the app is open, tapping opens the chat immediately; if resuming, it routes to the correct conversation instead of the legacy matchups page.
- Emoji reactions with persistence. We added schema support and fixed abstract-class issues so reactions store reliably. Badge counts update when reactions arrive, not just when messages do.
- Presence correctness. We improved the online tracking and reporting so that you know when the person you’re chatting to is actually online.
- Unread logic that matches user intent. Entering a chat now tracks consistently, so unread badges disappear as soon as the chat is viewed. This fixes the long-running “badge stuck” complaints.
- Added “Report Score” button to the Profile tab If the user / team is elligible to challenge the team / user profile they are viewing, then a new button allows a score to be entered without going through the challenge / chat invitation process.
Key takeaway: The goal wass to keep chat traffic on the app, rather than having players meet on Sportch and then revert to other chat apps for convenience. All together these changes improves the Sportch chat UX – bringing it to a level that competes with the best chat apps out there.
Developer Experience (DX)
One of the key limitations in any development experience – be it improving UX or otherwise – is speed of developer iteration. I.e. the speed at which the developers can test and trial new code, new UI elements, systems and conventions. The implication of this led us to coin the phrase “Developer Experience”, or DX, as a specialised form of UX. In a way, developers are a type of user of the app, with their own user-journey. With this understanding, dev-ops becomes intimately linked to UX; by enabling better DX.
And so we also improved the DX on a number of fronts:
- Single source of truth. ai-docs now include route maps, schema/queries, chat-server flows, and data migration notes. The goal: paste-ready context so fixes don’t diverge between web, mobile, and API layers.
- Why-annotations and intent. The
why_annotation_guideencourages@whycomments that explain intent, not just behavior, so Copilot/GPT sessions keep business rules (SLVC, team IDs, wildcard limits) in view. - Operational runbooks. Logging locations, test harness expectations, and deployment checklists are codified, reducing the need to rediscover paths or payload shapes when debugging ladder filters or chat presence.
- CI/CD and deployment scripts. By implementing github workflow actions along with deployment scripts we cut down deployment times to various environments and platofrms by a significant percentage. During v1.0 deployment would take days; as long as a week for a full deployment. After our CI/CD treatment deployment was reducted to minutes for any given platform / environment compbination. This was crucial for iteration speed for app that has three environments (dev, UAT and production) and three platforms (web, iOS, Android).
- Context Engineering by making judicious use of AI-generation via LLMs in a variety of ways; firstly for documentation and code-analysis and secondly by automating development tasks (under strict supervision and review) by user of LLM generation. This was crucial because the developer who created Sportch v1.0 tragically suffered a brain tumour which made knowledge transfer a real challenge – one that AI helped us overcome in a much smaller time-frame than would have otherwise been possible.
A key area was automated testing – we created a test framework that could be accessed by the LLMs directly, which allowed faster iteration by LLM generation because the LLM could test that the outcome matched expectation. This is especially practical on the backend API.
Key takeaway: Good DX depends on speed of both iteration and knowledge transfer. By front-loading intent into ai-docs and @why notes, we cut “what does this do?” loops and keep assistants aligned with domain knowledge and established methodologies. And by implementing solid CI/CD we cut iteration times by multiple order of magnitude.
V2.0’s UX / DX improvements weren’t just polish—they were architectural. By remapping identities, centralising ladder logic, and enriching notifications, we managed to keep established UX expectations, enhancing these in intuitive ways with new features.