Skip to main content

Command Palette

Search for a command to run...

10 Technical Wins from Building LigiPredictor.com

Updated
3 min readView as Markdown

10 Technical Wins from Building

After a few months of building, iterating, and deploying a full Premier League prediction game end-to-end, I took a step back to reflect on what actually worked. Here is my ranking of the top architectural and tooling decisions that paid off the most:

1. Automated CI/CD: A true game-changer. GitHub Actions -> DockerHub -> DigitalOcean Droplet via SSH, with Nginx handling the reverse proxy and auto-SSL. Building this pipeline early on made deployments effortless.

2. Custom Either Monad: I wrote a custom implementation for Railway Oriented Programming at the API boundary. It handles control flow beautifully without needing external dependencies like Vavr, keeping standard exceptions strictly inside the domain.

3. Clean Architecture & Use Cases: The MVP of the project. Encapsulating business logic in Use Cases allowed me to pivot the UI entirely (from Next.js to HTMX) without touching a single line of core domain logic.

4. jOOQ for the Heavy SQL: The leaderboard requires paginating 10k+ results, calculating previous round movement, and finding specific user positions. jOOQ’s type-safe Window Functions and CTEs handled this effortlessly.

5. Testcontainers over H2: Going with a real PostgreSQL container for integration testing from day one eliminated database dialect headaches. It also made me appreciate "Chicago-style" (state-based) testing over heavy mocking.

6. Gameweek Standings Engine: I built a custom rules engine to calculate actual gameweek standings, complete with complex Premier League tie-breakers (goal difference, head-to-head). Getting this right was tricky but highly rewarding.

7. Modular Match Importer: Built a robust importer to pull matches from an external API and save them gameweek by gameweek. It maps teams, scores, venues, and kickoffs into the DB in a clean, maintainable way.

8. Makefile Guardrails: I leaned heavily on Makefiles to orchestrate Docker, jOOQ codegen, and environment switching. To prevent disasters, the test DB is the default, and targeting prod requires explicit confirmation with terminal 🔥 emojis.

9. HTMX + Alpine + Thymeleaf: Pivoting away from a heavy SPA to a monolithic UI was a breath of fresh air. It made iterating, testing, and deploying incredibly fast.

10. Smart API Polling: To save resources, the background match scheduler dynamically adjusts its polling frequency from every 12 hours down to 90 seconds, depending on whether a match is currently live.

🏆 Honorable Mentions:

  • Demo Data Simulation: Built a simulation engine to generate fake seasons, users, and predictions so I could test the UI/UX without waiting for real matches.

  • YAML Seeding: Clean, maintainable referential data setup (leagues, teams, seasons).

  • Liquibase: It just works.

  • Multi-Module Design: Kept boundaries strict and dependencies clean.

  • Emergent Domain Design: Simplifying the domain over time to make tests easier to write.

It’s amazing how much you learn when you step outside standard CRUD tutorials and build a walking skeleton end-to-end.

Code is public here: https://github.com/ckagiri/ligitabl

#SoftwareEngineering #Java #SpringBoot #CleanArchitecture #HTMX #jOOQ #SystemDesign