JustFitness Logo

JustFitness

loading bar

Version 3 is Almost Ready

Explanation, Expectations, and the Launch Plan

Updated: 6 June 2022

The Gist

  • 4 rewrites to fix a bad foundation and maximize performance.
  • What you can expect in v3.
  • 3 Staged Release Plan (web, android, iOS)

Recap

  • Progressive Web App technology is not compatible with my vision for the app. The mobile versions need to provide haptic feedback and notifications which are not standardized features across web browsers.
  • The App Store and Play Store require the use of their respective payment processors if you want to accept payments though your app. I intended to find a way for you to subscribe on one platform and use the service on another in case you decide to switch between the two.
  • I intended to release beta versions for iOS and Android at the same time.

How'd that work out?

It didn't. I ended up rewriting the application 4 times to either make up for poor design choices or to improve performance. As for the payment processing problem, I've since abandoned the proposed solution for the sake of simplicity.

Rewrite 1: Fixing a bad foundation.

Shortly after the Elephant in the Room post, attempted to add a feature that would allow you to quickly switch between weight units while on the workout screen. It was for those of us with access to a gym with imperial and metric plates. One day I picked up a plate that I thought was 10lb but was actually 10kg. I hadn't noticed until I was already a few sets into an exercise and the mistake threw off my workout because it caused me to train harder than I intended. Of course I now know to pay attention to what I grab in the future, but In the scope of app, an optional button in the menu of the workout screen would be nice. Without it, the alternative would require the following steps on a small (mobile) screen

  1. Tap the back button or left arrow icon to exit the workout.
  2. Tap Dashboard in the bottom menu if required.
  3. Tap the cog icon to go to the Settings.
  4. Tap the lb/kg switch to switch to metric mode.
  5. Tap the play icon in the bottom menu to return to the workout screen and resume your workout.

Doing this multiple times during a workout would cause what I would consider to be an unnecessary amount of cognitive load in an application that is designed to have as little interactions as possible. I believe software tools should induce and work with flow states not weaken and work against them.

Implementing this feature did not go well. In fact, the process highlighted a major flaw in the core of the application. Even though I set up a rigorous automated testing system, I still had to play whack-a-bug which resulted in a complete restructure the application.

Why?

In version 1 your sets looked like this:

{
    reps: 5,
    weight: 135,
    ...
}

In version 2 they looked like this:

{
    reps: 5,
    lb: 135,
    kg: null,
    ...
}

Do you see the problem?

Versions 1 and 2 relied on your configuration while the app was running. If you switched to kg during a workout and selected a set from your history, the app would called a function to check for a non-null kg value. If the value was null, it would convert the lb value to kg and serve the result. Given that the set data model is the lifeblood of the application, the game of whack-a-bug was inevitable. The kg value should have been there from the start. In the end, I've decided to make the app log and recall your weights in pounds and kilograms at the same time like so:

{
    reps: 5,
    lb: 135,
    kg: 61.23496995,
    ...
}

I opted for such high precision so that when I discover an acceptable export model, we can both rest assured that the data is accurate. To cover the data from the previous versions, I had to write a script that goes into each set log in each session in each account and migrate their structures to the new model. The script doesn't change your data. It just modifies a copy and inserts it into another database table. If you're curious about this sort of thing, look up json schema validation and ajv.

That was just one of the many issues I had to contend with. Of the four rewrites; going back and thinking through the data model took the longest. Once that was taken care of, I then turned my attention towards maximizing performance.

Rewrite 2: Improving Performance

At this point the entire application ran on the main thread. Essentially it ran like a restaurant with one person in charge of taking orders, cooking meals, serving, checking out, and cleaning up. It made the app appear to be slow even on high-end devices because the app can only do one thing at a time.

The solution was to hire more staff or more specifically, move all computations unrelated to the user interface to a separate thread in a web worker. That way when you ask the app to go through your entire workout history and make a list of every exercise sorted by the number reps you performed; it will do so without causing the interface to jitter.

This time was also spent learning Typescript. Even though valid JavaScript is valid Typescript, it still wasn't as simple as a quick copy-paste.

Rewrite 3: Rust and WebAssembly

To squeeze a bit more juice from the performance lemon; I decided to rewrite all of the computation functions in rust and compile it to wasm. The initial goal was to simply use the wasm module as a library to call for only the heaviest functions. I didn't understand rust when I started so I took the opportunity to get used to the language. But it introduced what I like to call the mailman problem. Imagine having to send a loaf of bread, and the jars of peanut butter and jelly to your buddy's house with a request for them to send back a sandwich. Though a fun little exercise in futility, it is a huge waste of energy. For a technical explanation, read Interfacing Rust and JavaScript.

Rewrite 4: Maximizing Performance

Below is an excerpt from aforementioned link.

As a general rule of thumb, a good JavaScript↔WebAssembly interface design is often one where large, long-lived data structures are implemented as Rust types that live in the WebAssembly linear memory, and are exposed to JavaScript as opaque handles.

The app did not conform to this design. It would send data back and fourth across 2 barriers (web worker + wasm) just like in the sandwich analogy but with an extra courier. By the time I finished porting what I needed, I had gained enough experience to implement a proper design. The only solution was to have the data "live in rust" and only request what is needed for the UI.

4 Rewrites? Really?

Yes. I started this project back in 2018 and at the time I did not know how to program for the web. Prior to this, I attempted to make a game with in Java with LibGDX and I used flutter to build a plate calculator. Neither of those projects were ever finished or released but the plate calculator did become GymTools.io which only exists as a means to learn how to deploy a website.

Building this application has been a complete trial by fire and I've truly enjoyed every step of the way. More on that in another post. As I fixed my mistakes and grew as a developer and designer, I took the opportunity to add a few game changing features than I'm sure you'll have a hard time living without.

Without further adieu here's...

What to expect from JustFitness 3.0

Please note that the items outlined below are not a full reflection of the changes to come. They're just the parts I'm really excited to share with you.

Multi-Week Programs

This one was the most requested feature. Any lifter past the beginner stage knows that value of periodization training. Add up to 8 weeks and 7 days per week to each program.

Complete Color Customization

The defaults colors are tailored to my preferences but JustFitness is designed to work for you. So if you don't like them or if you're color blind; expect 4 different ways to personalize your experience. In addition to clicking or tapping, customize your muscle groups and tags via hex, HSV or an HTML color name. This addition may be perceived as a waste of time, but in my commitment to deliver an unparalleled workout app this was an easy addition.
ultimate color picker

Notebook Mode

The session editor needed a bit of work. Actually that's an understatement; it was really hard to use. There are a ton of workout apps out there in the wild but I still see people using a note application on their phone. Maybe they don't like workout trackers. Perhaps they've yet to find a good fit. In either case, version 3 takes that workflow to its logical conclusion by turning your text area into a direct import mechanism. Heck; At this point you don't even have to use the live tracker. If you prefer to log your sets in a physical notebook, you can transcribe your workout at any time.

Better Exercise Grouping

Instead of tapping and clicking to group exercises into supersets and circuits; simply add a group container and drag your exercises in.

Better Set Planning

Natural language input just got a lot better. Before, you were limited to just one input field to type 5×5; now, every set is an input field where you can add more sets at any insertion point by simply prepending n* where n is the number of copies of what follows. Additionally an exercise will no longer require you to open and close them individually. Instead, edit them all at once in a slider. Keep in mind that I removed the set editor popup. JustFitness is designed for you to spend as little time in it as possible so in terms of speed, typing beats clicking and tapping on any day.

Overhauled Exercise Statistics

Comprehensive statistics just like the Dashboard but with a few added metrics and consolidated comments.

Native iOS and Android Notifications

This feature isn't really as big of a deal as I've previously made it out to be. But I'm displaying it here to demonstrate the potential for future features that would utilize platform specific functionality like watch integration.

Release Plan

Version 3 will be released in 3 separate stages. Web, Android, then iOS. The web portion will not have Progressive Web App (PWA) functionality and will be hosted on a new subdomain lift.justfitness.io. The app subdomain will still be available but only for a limited time. The service worker on your device might still run the application but the connection to the server will not work. More on that in another post.

The iOS and Android apps are mostly finished. I just need to test the life cycle events and storage capabilities. Of course since these two platforms are uncharted territory, I really want to take extra care to ensure that the app does not violate their respective terms of service.

Thank you

Thanks for taking the time to read this post. Building this application has turned out to be far more challenging (and intrinsically rewarding) than I had initially anticipated. I can hardly wait to get your hand on this update and more importantly your feedback in response.

Thanks for using JustFitness to track your workouts. To those of you who have taken a leap of faith and have subscribed in spite of its flaws; I want you to know that I would not have had the heart nor the gumption to push toward this finish line without your generous support.