A database built for a legacy CMS
Polish table and column names, undocumented numeric statuses, tag relations that had drifted and raw FCKeditor HTML - with no schema documentation or types.
Case study · Sport & Technology
A new PLK.PL - three applications, the complete competition archive, live scores and sporting data engineered for match-day traffic peaks.
The Polish Basketball League ran a site that had been live for well over a decade: news, fixtures, standings, statistics, team and player profiles, galleries and video. It was the central source of competition information, but technologically belonged to another era.
Polish table and column names, undocumented numeric statuses, tag relations that had drifted and raw FCKeditor HTML - with no schema documentation or types.
News, videos and galleries had no slugs and existed only under numeric identifiers.
Fixtures, rosters, statistics, box scores and play-by-play came from an unusual external API with no intermediate layer.
Match-day traffic grows sharply while scores must stay fresh. Querying the source on every visit could never scale.
News, galleries, video, banners, partners and static pages were managed in ageing tooling.
We rebuilt the frontend, API and editorial panel on the same production database. New entities map onto historical tables, avoiding a one-off, high-risk migration of the entire archive.
Every result, table and statistic comes from ESOR, the Polish Basketball Federation's system. The source could not be changed, while the site could not be slower or less available than it.
One API entry point, form-encoded POST requests, operations passed in the body, and no versioning, types or contract. Responses reflected an internal record-keeping model rather than a public site's needs.
13 modules for leagues, seasons, rounds, games, teams, players, coaches, standings, statistics, venues and search. The frontend never sees raw ESOR responses.
Every request has a deterministic memcached key and lifetime, with controlled refresh and bypass mechanisms.
Heavy queries rebuild every four minutes and live games every minute - before a visitor asks for them.
Only instance zero refreshes data, so scaling the process count never multiplies traffic to ESOR.
Incremental regeneration every 60 or 300 seconds adds an independent layer between the visitor and backend.
The site remains fast when its source is under load, and new statistics views use ready, typed modules instead of direct calls to a foreign API.
A league site is judged on match day. A live score must be visible immediately on every page, regardless of how many fans are watching in the same minute.
The API asks ESOR for games in progress every 60 seconds while browsers perform no polling.
Only cluster instance zero fetches fresh state, while every process can serve it.
Live state merges into fixtures: status, quarter, clock, score and broadcast URL share one model.
Live games and standings follow fans through articles, player profiles, fixtures and archives.
The carousel finds the live game, marks it with a pulse and expands into the complete round and standings.
The cost of live scores does not grow with traffic. Tens of thousands of sessions still produce exactly one request per minute at the league system.
PLK.PL uses a dedicated design system developed and versioned with the application. The design was a system from which views are assembled, not a collection of isolated mockups.
The accent scale covers the full interaction cycle, surfaces define depth levels, and signal colours communicate status without reading.
Three type families: one for content and two monospaced faces for scores, the game clock and statistics tables.
Content - Inter
Another set of points at the Kolejowa arena
Headings, game previews, articles and navigation. The face remains readable in small labels and dense table layouts.
400 · 500 · 700 · 800Data - Roboto Mono / Geist Mono
| Player | PKT | ZB | AS | EVAL |
|---|---|---|---|---|
| A. Kowalski | 24 | 7 | 5 | 28 |
| M. Nowak | 18 | 11 | 2 | 25 |
| P. Zieliński | 9 | 3 | 8 | 17 |
monospaced figures - numbers align in columnsVariants, sizes, focus and disabled states come from the system rather than local overrides.
Quarter 4 · 02:14Radii, shadows, focus rings and motion are tokens too.
0.125rem0 2px 0 #F8F8F8ring 2px #F88D65180° → #0E0C1DloaderLine 1s linear159 components behave like one product across 71 views - including post-launch additions such as the play-off bracket and U23 table.
The new site was never handed a clean schema. We mapped 30 entities onto legacy CMS tables and translated Polish columns into a readable domain model. Versioned migrations introduced every change at deployment.
Media is converted to WebP and resized on upload while preserving the old CDN structure, so archival photos and logos remain at unchanged addresses.
OpenAPI is the contract between applications. A backend contract change becomes a frontend compilation error instead of a production defect.
A merge into the development branch publishes staging; a merge into main publishes production. The pipeline compares the commit with the last build and rebuilds only the applications that actually changed.
Database migrations run before the new API starts. Every frontend build lands in a separate release directory with a shared image cache, and clustered processes reload without downtime.
A steady rhythm of small changes across 1,230 commits instead of rare, high-risk deployments.
One process deploys frontend, backend and admin panel while rebuilding only changed applications.
71 public views, including the complete season archive tree, run in production.
The new site uses the same database with no risky, one-off content migration.
Slugs, metadata and Open Graph also cover years of content that previously existed only under IDs.
Quarter, clock and score refresh every 60 seconds with one source request per minute.
Tokens and shared components cover all 71 views and evolve with the product.
Two cache layers and pre-emptive refresh isolate fans from external API load.
Rankings, records, shot charts, play-by-play, CSV and PDF work across current and historical seasons.
59 components manage content, media, advertising, partners, play-offs and configuration with permissions.
The bracket, voting, rankings and individual share images turn predictions into participation.
New seasons, Cup, Super Cup, U23 and calendar export arrive without downtime.