bene : studio is a global consultancy, helping startups, enterprises and HealthTech companies to have better product

Building a smart running app with personalized training plans


About Coopah running app

Coopah is a personal running coach application helping runners achieve their running goals with personalized training plans and digital training groups in an easy-to-use app.

The product is built to fill the gap in the market between private trainers and free pdf training plans. Many people can’t afford £100-£200 per month for a running coach, and the pdf plans are hard to access on your mobile and, crucially, do not adapt to how you are training or are personalized to your current ability.

Project summary: from zero to MVP, then to a ready-to-use app

At the start of our cooperation, we built an MVP prototype for our partner. After the success of the prototype, we could start working on the UI/UX design and then the app and its features.

Technically we set out to build an application that could generate a fully personalized running training plan for each user and adapt as they ran. We use machine learning to be able to predict what training sessions will give any individual with specific attributes the best chance of success. The machine learning feature collects data from athletes using Coopah and involves publicly available training data from runners worldwide.

Before getting to this point, we needed to build a base application that will allow the customization of training plans based on a user’s attributes.

We have been building a front-end application on Flutter, making the app cross-platform, and allowing it to be released to the Apple App Store and Google Play store. The frontend application enables users to enter information about themselves and select a target race and time they want to achieve.

The backend, built on Node.js, uses the users’ attributes to find one of 250 different training plans that should be used as a base training plan for them. The users then follow their training plans and import their training from their Garmin accessory or enter them manually. The backend uses a custom-built algorithm to calculate the user’s threshold pace. This pace would change as the user runs. As the threshold pace changed, the algorithm would adjust the session speed for the user, giving them a fully personalized experience.

4 major development milestones supporting business goals

Creating a Dynamic Training Plan with a custom algorithm

When creating a dynamic training plan, we had some challenges to face: 

  • Every user had a different race day. How would we be able to create a training plan that accounts for every possible starting and ending date for multiple distances? We decided to create training plans that were between 6 weeks and 24 weeks in length and covered 4 key distances. We then looked at the starting week and last week of these plans and made them adaptable so that depending on the date the user entered the first and last weeks would remove sessions from the plan. 
  • How could we vary the difficulty of the plans based on the users’ threshold pace? The application needed to be able to give training plans that were suited to people of all abilities. As we were only building one training plan base per week/distance, we needed to vary it by the pace of the sessions. In the future, we will add more variables to this. The challenge was how we can use simple equations specified in the training plan to create time and add variables to these equations. We executed it by parsing equations as regular expressions and substituting the variables with values from individual user properties, for example, average speed and threshold speed.
  • Once created, the training plan was working well. But what if some user wants to update the training plan? Simple cases are just matters of parameter updates. The challenge was that when the training plan length needs to be changed, it makes the update difficult since new training sessions should be added to the actual plan. We executed it by creating a new training plan from scratch and adding old sessions as history. Also, we moved parameters from the old plan to the new one.

Tailor made subscription model – RevenueCat integration

Initially, upon completing registration, the application had free access with limited content available and also a priced monthly subscription to provide extended features.

More subscriptions were added with different prices to customize the paid content further. These were then dynamically offered to users based on their personal data, e.g. which running club they are part of.

To overcome the issue of managing the many types of subscriptions, RevenueCat was integrated. For the mobile app, we chose the Flutter SDK, this can be installed as a package. At this point, the app already had paying customers whose subscriptions we had to preserve. This meant that when switching to RevenueCat, parts of the codebase were refactored, but the same functionalities were kept. 

With its organized subscription model and easy-to-use dashboard site, tracking and managing revenue was no longer a hassle. Internal mobile devices were used to test these subscriptions.

Garmin integration

This was the largest and most complex development we worked on in this project. The goal was to allow users to use their Garmin watches to record their sports activities and

When we first developed the application, we asked users to manually enter the details of their training sessions into the application. We found that only 6% of users were filling in their details. We conducted user testing and realized that people found the manual entry too long and not worth the effort. We decided to look at integrating Garmin to help automatically get their data into the application. 

This involved changing how the application worked to allow users to connect their Garmin device, select a session they have run, and add it to the app. 

Many users have and actively use fitness watches to track their sports activities. Garmin was chosen as a fitness activity provider to enable users to create and receive their recorded Garmin activities while using our application, as it was the most popular device used by our existing user base. 
To integrate Garmin, one of the challenges we faced was knowing the length of time, pace, and distance that the user ran for each of the intervals that the app had scheduled. Garmin provided us with intervals through an API, but there were some occasions where users would create multiple intervals for one training session. This is caused by the auto-lap feature, enabled by default on almost all watches. It essentially means that long distances are automatically split every 1km/mile. It may be advantageous to many users, but to our requirements, it meant that all long-distance Garmin activities will have more intervals than the corresponding Coopah session. We built an algorithm that matched the Garmin intervals to Coopah intervals. If there were multiple unmatched intervals, it averaged those remaining ones and put that into the last interval’s place. If Garmin intervals were less than Coopah intervals, then the first N places were filled, and the rest were left unmatched.

Enhancing user experience with social running “clubs” backed by fast and secure solutions

One of the core strengths of the application is the community, so the client wanted the users to feel they belong to a group of like-minded individuals. By allowing them to join clubs (either during registration or later in-app), users could experience the uniquely designed mobile look to match the colors/designs of the given club. Besides this, users can chat with each other through StreamChat, see all club members, and a custom image gallery.

One challenging part was that there could be clubs with more than a thousand members. It would not be efficient for 1000 user profiles to be queried from the server every time a user opens this page. Not to mention that users usually have high-quality avatars, which should be secretly accessed and not public on the internet. Pagination was introduced with a 10-member limit in a scrolling view to make it work seamlessly. Also, to counter the high-res images, user avatars are stored in AWS buckets, and only the secure links are in the response of a user profile.

Another exciting aspect of this feature was the design part and the dynamic coloring of the application’s look. Different color schemes and design elements had to be tested beforehand to give users the best experience.

Development process

Every new feature we developed went through the following iterations:

  • Initial requirements were drafted
  • Requirement and feasibility analysis (if needed)
  • Requirement finalization
  • UX/UI preparation (if needed)
  • Development
    • Code quality assurance
    • Functionality testing
  • Internal testing in a demo environment
  • Feedback and fixes (if needed)
  • Release to production/app stores
  • Production testing
  • Live feedback and fixes (if needed)

Development team

In this project, we provided a UX/UI Product Designer, Backend and Frontend Mobile Engineers and a Project Manager.

BS_JoinUs_Gabor Paholics

Gábor Paholics

Backend development

BS_JoinUs_Daniel Fodor

Dániel Fodor

Full-stack development

BS_JoinUs_Aliz Moldovan

Alíz Bene

UX/UI design


Dávid Szalay

Project management

List of technologies

Mobile application

  • Flutter framework (Dart language)

Admin web application

  • React
  • TypeScript
  • Nginx


  • Node.js
  • TypeScript
  • PostgreSQL
  • AWS EC2


Let bene : studio enhance
your digital product!