My Marathon into Spring Boot: Building a Strava Empire, One Bug at a Time

Vaibhav MagdumVaibhav Magdum
7 min read

They say the best way to truly learn a new technology is to build something you're passionate about. For me, that meant finding a way to connect my professional ambition—mastering the Spring Boot framework—with a personal obsession: running. As an avid runner who lives and breathes by my Strava stats, and a developer hungry for a real, hands-on challenge, the path was clear. I decided to merge my two worlds and create Run-to-Own, a project that started as a learning exercise and quickly became a testament to the agonizing, rewarding, and often absurd reality of software development.

The idea was exhilarating: what if every run you logged on Strava wasn't just data, but a move in a massive, global game of territory control? This wasn't just about learning the intricacies of Spring; it was about creating the app I've always wanted as a runner. The vision was to transform the familiar map of my city into a dynamic game board of hexagonal tiles. Every GPS-tracked run would sync from my Strava account, converting my path into territorial claims. Run the most in a tile, and you own it—until a rival challenges you for control. This was more than a project; it was a quest.

The First Sprint: From "Hello World" to a Working Dashboard

Every marathon starts with a single step, and for this project, that was bootstrapping a new Spring Boot application. I dove in headfirst, setting up the backend with a PostgreSQL database and tackling the first major hurdle: the Strava OAuth 2.0 authentication flow. I still remember the thrill of seeing that iconic orange Strava authorization screen pop up for the first time, asking for permission to connect to an application I had built. When it redirected back to my localhost with an access token, and I successfully made my first authenticated API call to fetch my own profile, it was a moment of pure magic. Seeing my own name and profile picture appear in my own creation was the fuel I needed to keep going.

With the core connection established, I focused on building a user experience that felt modern, personal, and deeply integrated with the Strava aesthetic I loved. I designed a polished, dark-themed UI for the home and profile pages, complete with a slick loading animation to make the initial data fetch feel seamless. The profile page quickly became my personal dashboard, a place where I could see my lifetime running stats—total distance, elevation gain, and hours spent on the trail—all beautifully visualized with ApexCharts. The backend ProfileController was humming, making calls to Strava's API and serving up the data perfectly.

One of the features I was most excited about, and one that turned into its own mini-saga, was gear tracking. As any runner knows, you form a bond with your shoes; they're your partners in every journey. Getting the gear data was a lesson in the realities of third-party APIs. The primary athlete stats endpoint didn't include gear by default. I had to dig deeper and found that a more detailed athlete endpoint returned a richer object containing nested lists of bikes and shoes. Writing the Java code to parse this complex JSON, handle potential nulls, and then display it in clean, simple cards on the profile page was a major win. Seeing my trusty ASICS Gel Nimbus 26, with over 16,000 kilometers of virtual mileage, show up on the screen was a deeply satisfying moment. I wasn't just building an app; I was building my app. At this point, I felt like I was flying.

Hitting the Wall: My Agonizing Battle with the Map

Then, I started working on the core feature: the interactive territory map. And I hit a wall. Hard.

My goal was to take the summary_polyline from a Strava activity—an encoded string of latitude and longitude coordinates—and translate it into a visual representation of conquered territory on a map of hexagonal tiles. It sounded straightforward. It was anything but.

This began a multi-day odyssey into what I can only describe as the "Silent Rendering Failure." It's a developer's nightmare. My backend TileService was working flawlessly. I could see the Hibernate logs firing, creating new Tile and TileUserStats records in my PostgreSQL database. My frontend API call to /api/explore/tiles was returning a perfect JSON array of H3 indexes. And yet, the map remained stubbornly empty. There were no errors in the Java console. No errors in the browser console. Just a blank map staring back at me, mocking my efforts.

This sent me down a deep and frustrating rabbit hole of client-side debugging. We tried everything:

We started with a simple stack: MapLibre for the map and h3-js to convert the H3 indexes from my backend into GeoJSON polygons. I spent days debugging the GeoJSON format, ensuring the coordinates were in the correct [longitude, latitude] order and that every polygon loop was "closed" by repeating the first coordinate at the end. Still nothing.

We thought it might be a library loading issue. This led to a new class of errors, like rewind is not defined, which turned out to be caused by browser extensions blocking the external scripts I was using to fix the polygon data. We even tried to manually implement the "winding order" logic inside the JavaScript, but the silent failure persisted.

Convinced the issue was with the complexity of drawing so many shapes, I decided to upgrade my stack to a more professional architecture using the Deck.gl library. It has a specialized H3HexagonLayer designed for this exact purpose. The code was cleaner, the approach was more powerful. And still... nothing on the map. The silence was deafening. The console logs showed a perfect array of data being passed to the layer, and then... nothing.

The Pivot: When All Else Fails, Rethink Your Architecture

At this point, it was clear that client-side rendering was too brittle and unreliable in my specific environment. The expert research I found confirmed my fears: I was fighting a deep, silent incompatibility between the libraries, likely related to incorrect GeoJSON "winding order" and a failure to project coordinates to the required Web Mercator (EPSG:3857) format—a mandatory step for all web map rendering that often fails silently.

The only way forward was a complete architectural pivot: server-side rendering. The plan was to have my Spring Boot backend do all the heavy lifting. It would generate highly efficient Mapbox Vector Tiles (.pbf files), and the frontend's only job would be to display them. This is the architecture used by professional mapping services, and it would bypass the client-side chaos entirely.

This led to a new kind of battle: Maven dependency hell. A series of attempts to add a Java library for creating vector tiles was blocked by BUILD FAILURE errors. It seemed every library I tried was obsolete, unavailable, or had incorrect documentation. We were stuck in a loop of Could not resolve dependencies errors, trying libraries like de.j-lawyer:j-mvt, com.graphhopper:graphhopper-mvt, and various Mapbox SDKs, all of which failed to build. It felt like I had traded one unsolvable problem for another.

The Breakthrough and What's Next

After a final, exhaustive deep dive, we found it. The definitive, modern, and—most importantly—available Java library for the job (io.github.sebasbaumh:mapbox-vector-tile-java). With the correct dependency and the corresponding Java code, the backend finally compiled successfully, and the path forward was clear.

This journey has been a powerful, hands-on lesson in the realities of modern software development. It's taught me more about Spring Boot, Maven, and the complexities of geospatial data than any tutorial ever could. It's been a marathon of debugging, a steep learning curve, and a testament to the fact that sometimes the most rewarding challenges are the ones that push you to the brink of giving up.

The backend is now stable and ready. The next step is to build the simplified frontend and finally bring the "Run-to-Own" map to life. After that, it's on to building the features that will make this a true community experience: leaderboards that rank players by territories owned, a streak system to reward consistency, and a friendship system to challenge your rivals.

This project started as a way to learn Spring Boot, but it's become so much more. It's a fusion of my passions, a testament to the power of persistence, and a reminder that even when the map is blank, you have to keep running. Thanks for following along. Stay tuned for the next update!

0
Subscribe to my newsletter

Read articles from Vaibhav Magdum directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Vaibhav Magdum
Vaibhav Magdum

Mastering Data Science, Securing Networks, and Diving into DevOps I’m all about turning data into actionable insights with machine learning and data visualization, but my tech curiosity doesn't end there. I’m also diving into network security, building digital defenses as solid as Van Dijk’s game on the pitch. Now, I’m adding a new layer to my tech stack with DevOps and AWS! 🌐 From automating workflows to managing cloud infrastructure, I’m eager to streamline and optimize like never before. Collaboration is my thing, and I bring a methodical approach and an eye for detail to everything I tackle. When I’m not decoding algorithms or shoring up cyber defenses, you’ll find me strategizing on the football field (YNWA!), playing chess, or jamming to Patrick Watson. These passions fuel my creativity and critical thinking, crucial for navigating the fast-paced world of technology.