<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Articles by Tom Norton</title>
        <link>https://www.tpjnorton.com</link>
        <description>A blog about all things code, design and software engineering.</description>
        <lastBuildDate>Fri, 12 Jun 2026 13:11:58 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>Copyright © Tom Norton</copyright>
        <item>
            <title><![CDATA[Using Math to Predict My First Half Marathon]]></title>
            <link>https://www.tpjnorton.com/blog/posts/predicting-my-first-half-marathon-time</link>
            <guid>https://www.tpjnorton.com/blog/posts/predicting-my-first-half-marathon-time</guid>
            <pubDate>Sun, 07 Jun 2026 12:00:00 GMT</pubDate>
            <description><![CDATA[On a whim, I signed up for the Lausanne Half Marathon and I have never run further than 13 km. So I built a research-backed race time predictor and ran my own numbers through it. Here is what it says.]]></description>
            <content:encoded><![CDATA[
![header photo](/images/blog/race-predictor.png)

It's time for another edition of nerding out on sports data!

On a whim, I signed up for the [Lausanne Half Marathon](https://en.lausanne-marathon.com/) this October. The longest I have ever run in one go is 13 km. So I have an obvious question to answer before training starts: _what is a realistic target?_

I am a cyclist by default. I know how to look at a course, look at my power numbers, and work out what I should do on race day. For running I have nothing equivalent. Pace charts and "just train at your goal pace" advice felt like guesswork. I wanted the same thing I have on the bike, a model that takes my actual numbers and the actual course and gives me a defensible target.

I could not find a free tool that did all of it in one place. So I built one. This post is me running my own data through it for Lausanne, including the bits where the model says I am being optimistic.

## The tool

[I built it here](/tools/running-race-predictor). It takes one or more recent race times and fits three models side by side:

- **Riegel (1977).** The textbook formula. Peter Riegel fit `T₂ = T₁ · (D₂/D₁)^1.06` against thousands of race results. The exponent 1.06 captures the slowdown as distance grows.
- **Cameron (1998).** A polynomial Dave Cameron fit to world record times. Tracks elite performances a little better than Riegel above the half marathon.
- **Personal fatigue exponent.** With two or more races the tool fits your own exponent by ordinary least squares in log-log space. If your real number is above 1.06 you slow down faster than the textbook says. Below 1.06 you hold on better.

On top of that it computes [Jack Daniels' VDOT](https://www.had2know.org/health/vo2-max-calculator-racing-daniels-gilbert.html) from your strongest race, the five Daniels training paces (Easy, Marathon, Threshold, Interval, Repetition), and two course adjustments: elevation gain via Minetti's 1.31 mL O₂ per kg per metre climbed, and race-day temperature via the [El Helou et al. (2012) marathon heat curve](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0037407).

Everything runs in the browser. No signup, no data leaves your machine.

## My numbers

My recent times, both run as standalone efforts on flat-ish loops:

- 5 km: **22:38**
- 10 km: **48:17**

I am writing them down because the rest of this post is meaningless without context, and posting a half marathon prediction without showing the inputs feels like cheating.

The first thing the tool does with these is compute VDOT from each one. The Daniels-Gilbert formula:

```
VO2cost(v) = -4.60 + 0.182258·v + 0.000104·v²
%VO2max(t) = 0.8 + 0.1894393·e^(-0.012778·t)
                + 0.2989558·e^(-0.1932605·t)
VDOT       = VO2cost(v) / %VO2max(t)
```

`v` is velocity in m/min, `t` is race duration in minutes. The first line is the oxygen cost of running at a given speed. The second is how much of your VO2max you can hold for a given duration (longer events let you sit at a lower percentage). VDOT is the ratio, a pseudo-VO2max that bakes in both your engine size and how efficiently you run.

For my 5 km at 22:38 that comes out to **VDOT 43.1**. For the 10 km at 48:17 the model lands at 41.7. The tool picks the higher of the two as the anchor, which means my 5 km is the reference race for the predictions that follow.

There is something interesting in that gap. If you take Riegel forward from my 5 km, the predicted 10 km is `1358 × 2^1.06 ≈ 47:12`. My actual 10 km is **65 seconds slower**. About 2.3%. So I am already falling off the textbook curve between 5K and 10K, and the half marathon is twice as far again.

## What the formulas predict

Three predictors, three answers for a 21.1 km target:

| Model | Predicted half |
| --- | --- |
| Riegel from 5K (E=1.06) | 1:44:07 |
| Cameron from 5K | 1:43:57 |
| Personal fit (E=1.093) | 1:49:10 |

Riegel and Cameron agree within ten seconds. That is what you would expect from two formulae fit to different data sets, applied near the source distance. The personal fit is the interesting one. With both my 5 km and 10 km on the chart, ordinary least squares in log-log space recovers a fatigue exponent of **1.093**, well above Riegel's 1.06.

The interpretation: I slow down faster than the textbook runner as distance grows. That is consistent with being a cyclist who has done a lot of high-intensity work over short distances and very little aerobic base. The 5K-to-10K drop-off is already steeper than someone with a deeper endurance background, so the model extrapolates a steeper drop-off out to 21 km. The 65-second gap at 10 km becomes a 5-minute gap at the half.

This is the insight Riegel-only calculators miss. The 1.06 default would tell me 1:44. The personal fit tells me 1:49 and explains why.

## Adjusting for Lausanne

The Lausanne course is not a flat oval. It runs along the lake, eastward from Place de Milan through Pully, Paudex, and Lutry to a turnaround near Cully at about 10.5 km, then back to finish at Place Bellerive in Ouchy. Total elevation gain is around 56 m and loss is around 93 m, so the net is a 37 m descent.

Late October on Lake Geneva typically lands around 10 to 14 °C at the morning start. I am going to assume 12 °C.

Both adjustments are layered on top of the base prediction.

**Elevation.** Minetti et al. (2002) measured the metabolic cost of running uphill at 1.31 mL of oxygen per kilogram per metre climbed, on top of the horizontal-running cost. For a net descent of 37 m at my race-pace VO2 cost (about 34.5 mL/kg/min for me at projected half-marathon pace):

```
extra seconds = 60 · (1.31 · Δh) / C
              = 60 · (1.31 · -37) / 34.5
              ≈ -84 seconds (raw)
```

The tool then halves the descent benefit, because braking forces and gait limits mean descending is not the mirror image of climbing. So the final elevation credit is around -42 seconds. Useful but not huge.

**Heat.** El Helou et al. (2012) fit a U-shaped pace-versus-temperature curve to 1.8 million finish times from six major marathons. The optimum sits near 10 °C. For 12 °C, the slowdown is `0.00034 · (12 - 10)² = 0.14%`. Effectively nothing. Lausanne in October is just about the easiest possible weather day for me.

Combine them on top of the 1:49:10 personal-fit prediction:

```
1:49:10 - 0:42 + 0.14% = 1:48:35
```

So the headline number my own tool gives me, on real numbers, for the real course, in expected weather, is **1:48:35**. Roughly 5:09 per kilometre. About a 20-second drop from my 10K pace of 4:50 carried twice as far.

## The big caveat

There is a problem the math does not see: I have never run further than 13 km.

The three formulae assume your training supports the target distance. The model has no way to know whether you have done the long-run work to hold race pace through km 18, 19, 20. Hitting the wall is not in the equations. Bonking is not in the equations. The polite term researchers use is "endurance reserve". The 1:49 personal fit prediction is what someone with my 5K and 10K times _should_ be able to do if they have built the aerobic base to back it up.

Right now I have not. So 1:48:35 is the right answer to a different question. It is the right answer to: _if I do the training, what is realistic?_ It is not the answer to: _what will I run on race day if I keep training the way I have been?_ For that one, I expect a 5 to 15 minute drift slower as my legs run out of glycogen and form. Anyone telling you they predicted their first half marathon to the minute without a long-run block first was lucky, not right.

This is the same trap that catches first-time marathoners who try to extrapolate from a 10 km PB. The model is honest, you just have to read the small print on it.

## What the tool also gives me

The other useful output is the training paces. Daniels' system gives five intensities, each as a percentage of VDOT:

| Zone | % VDOT | My pace (/km) |
| --- | --- | --- |
| Easy / Long | 65–79% | 5:14–6:07 |
| Marathon | 80% | 5:11 |
| Threshold (T) | 88% | 4:48 |
| Interval (I) | 98% | 4:24 |
| Repetition (R) | 105% | 4:09 |

These are the anchors for the next 16 weeks. Most of the mileage at easy pace, a weekly threshold session at 4:48/km, one VO2max session at 4:24/km, and progressively longer easy long runs. The goal between now and October is to push the long run out from 13 km to 18-19 km, which is the standard "long run goes to 90% of race distance" rule for first-time half marathoners.

If I do that work, the VDOT 43 prediction holds. If I do not, the model says one thing and my legs say another, and the legs win.

## Bottom line

The math says **1:48:35** for Lausanne, give or take, conditional on actually doing the training.

What I find useful is not the headline number. It is the personal-fit exponent of 1.093 telling me my drop-off curve is steeper than the textbook. That is a training prescription disguised as a prediction. The path from 1.093 down to 1.06 is more easy-paced mileage and longer long runs. Get the exponent down and the half marathon time drops with it, even if the 5 km PB stays the same.

I will run this again in late September on whatever fresher times I have, and see where the personal exponent has moved. If it is still at 1.09 I have not done the work. If it is closer to 1.06 then the model is telling me I am ready.

If you want to do the same, the tool is [here](/tools/running-race-predictor). One race result is enough to start. Two gets you the personal fit. Drop in your course's elevation gain and expected race-day temperature and you have a defensible target you can train towards. And if your personal exponent comes out wildly different from mine, I would be curious to hear about it.
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
        <item>
            <title><![CDATA[How Vibe Coding Made Me a Hobbyist Again]]></title>
            <link>https://www.tpjnorton.com/blog/posts/how-vibe-coding-made-me-a-hobbyist-again</link>
            <guid>https://www.tpjnorton.com/blog/posts/how-vibe-coding-made-me-a-hobbyist-again</guid>
            <pubDate>Wed, 20 May 2026 12:00:00 GMT</pubDate>
            <description><![CDATA[After years too busy for side projects, vibe coding got me shipping again. I've been building free tools for cyclists and climbers at the weekends, and I'm loving it.]]></description>
            <content:encoded><![CDATA[
![header photo](/images/blog/covers/tools.png)

These days, I have very little time to work on side projects, between work, sports, social life, etc. Life tends to get in the way. Earlier in my career, I used to be able to find time for little side projects, working on tools, etc. Since the arrival of Claude and agentic coding, it's finally possible to build these sorts of things without the same time investment. I get to focus on making tools to help me answer my own conundra, building stuff the same way I used to, small iterations, going feature by feature, focusing on correctness. I love building products, and I love tinkering with code, and I missed it. Vibe coding has given me the chance to get back to that, even if it's just for a few hours at the weekend.

Over the last six weeks I've been quietly publishing a small collection of free, browser-based tools for cyclists, climbers, and people training for endurance events. There are six of them now. Each one started with a question I had while training, where the answer either didn't exist as a free tool online or was hidden behind a SaaS subscription. They're all free and run in the browser, with no account required.

Here's a short tour.

## 1. Bike Split Estimator

![Bike Split Estimator screenshot](/images/blog/bike-calculator.png)

Upload a GPX route and the tool predicts your finish time at a given power output, broken down section by section. The physics covers air drag (CdA), rolling resistance, gravity, drivetrain loss, air density, and headwind. You can also reverse-solve it: enter a target time and it tells you the wattage you need to hit.

The route is split into sections based on actual terrain changes rather than equal distances, so a steady 7% climb shows up as one section instead of being averaged into the surrounding flats. For each section, a [Newton-Raphson solver](https://en.wikipedia.org/wiki/Newton%27s_method) finds the speed at which rider power matches total drag, accurate to a thousandth of a watt.

I built it because I'm doing the bike leg of a sprint triathlon this summer and wanted to model exactly what wattage I needed on the climb versus the flat to beat last year's third-place split. The closest paid equivalent is [Best Bike Split](https://www.bestbikesplit.com/) at around $19 a month.

I also wrote a [follow-up post](https://tpjnorton.com/blog/posts/testing-my-bike-split-calculator-against-real-data) where I validated it against four of my own rides. The model came in about 2 to 8% optimistic on real-world finish times, with the error growing on longer and more descent-heavy routes (because the model doesn't yet know about cornering).

[https://tpjnorton.com/tools/bike-calculator](https://tpjnorton.com/tools/bike-calculator)

## 2. Training Week Planner

![Training Week Planner screenshot](/images/blog/training-planner.png)

The most recent of the six, and the only one without any physics under the hood. It's a weekly calendar where you add people, drop in sessions with auto-picked emoji based on what you type, assign one or many participants per session, and drag sessions around to reorder them.

I built it because I needed somewhere to plan a training week for me and my partner. I'd tried a shared Google Sheet, a Notion page, and Apple's Reminders app. The sheet was readable but a pain to edit. Notion was the opposite. Reminders was fine for solo use but bad at "her ride, my run, joint swim on Sunday."

What I wanted was a planner that lived in a browser, saved locally without an account, and could be printed on one page for the fridge.

Things I'm pleased with:

- Type "deadlift" or "fingerboard" or "open water swim" and the emoji picks itself from a small keyword index
- Multi-person assignment with colour-coded chips, so a joint session reads at a glance
- Local storage only, so no account and no data leaves the browser
- ICS export, so a planned week can be loaded into a calendar app if you'd rather
- A print stylesheet that actually looks good on paper

[https://tpjnorton.com/tools/training-calendar](https://tpjnorton.com/tools/training-calendar)

## 3. FTP Calculator

![FTP Calculator screenshot](/images/blog/ftp-calculator.png)

Functional Threshold Power is loosely defined as the wattage a cyclist can sustain for about an hour, though [recent research](https://pubmed.ncbi.nlm.nih.gov/36803419/) has shown most riders only hold theirs for 30 to 50 minutes in practice, and the metric is fuzzier as a physiological threshold than its name suggests. It's still the anchor for most training-zone work, and most riders find theirs out by doing an 8-minute or 20-minute maximal test.

This calculator estimates FTP from a single max effort at any duration between 3 and 30 minutes, using a hyperbolic power-duration curve calibrated against the two canonical FTP tests:

- FTP = 90% of 8-minute power
- FTP = 95% of 20-minute power

Both rules assume an average rider. Cyclists with above-average anaerobic capacity (sprinters and anyone whose short-duration power sits well above population norms) [get an inflated FTP from the 8-minute test](https://www.highnorth.co.uk/articles/critical-power-calculator), because so much of an 8-minute effort comes from W', the anaerobic capacity that the flat 90% coefficient assumes to be average across riders. The 20-minute test is less affected but still imperfect. The calculator can take an explicit W' value to switch to a fully personalised Morton-3P solve for these riders.

Mathematically that's [Morton's 3-parameter critical-power model](https://pubmed.ncbi.nlm.nih.gov/8854981/) written in a scaled form where the anaerobic work capacity W' scales with FTP. The scaled form has a nice property: the duration coefficient depends only on time, so the same curve fits every rider.

If you have two or more efforts at different durations (say a 5-minute and a 20-minute), the tool switches to a proper 2-parameter critical-power fit and recovers both CP and W' from the data using ordinary least squares. With three or more efforts it also reports R² so you can tell when your inputs are noisy or your efforts were too similar.

Output includes FTP in watts, W/kg if you supply weight, a performance band from "Untrained" through "World class" based on [Coggan's published power-profile tables](https://www.trainingpeaks.com/blog/power-profiling/), and the full Coggan 7-zone training breakdown.

[https://tpjnorton.com/tools/ftp-calculator](https://tpjnorton.com/tools/ftp-calculator)

## 4. Cycling Drafting Calculator

![Cycling Drafting Calculator screenshot](/images/blog/drafting-calculator.png)

If you're sitting on someone's wheel in a paceline, how much wattage are you actually saving? Most of what's online is either vague or paywalled.

Give the calculator the leader's wattage and the group size, and it returns each rider's wattage in absolute watts and as a percentage of the leader, plus the speed the group is travelling at.

The drag-savings figures come from [Blocken et al. (2018)](https://research.tue.nl/en/publications/aerodynamic-drag-in-cycling-pelotons-new-insights-by-cfd-simulati/) wind tunnel and CFD measurements on full pelotons, plus on-road field studies. Position 2 saves about 27%. Savings deepen through positions 4 to 6, then taper. The leader gets a small ~3% bonus when anyone's sitting on their wheel (the trailing rider's low-pressure bubble pushes them along, a measurable effect in field data). The rider at the back of a long line of five or more loses a couple of points because there's no one behind them filling in their wake.

It's a flat-road steady-state model, so it won't tell you what happens on a climb. For a paceline on rolling terrain it's a useful reality check on what each pull is actually costing the rider on the front.

[https://tpjnorton.com/tools/cycling-drafting-calculator](https://tpjnorton.com/tools/cycling-drafting-calculator)

## 5. Bike Gear Calculator

![Bike Gear Calculator screenshot](/images/blog/gear-calculator.png)

A chainring-cassette-wheel calculator with the usual outputs: gear ratio, gear inches, development in metres per crank revolution, and a cadence-to-speed converter that works both ways. A reference table shows your speed at 60, 70, 80, 90, 100, and 110 rpm so you can see at a glance which gear puts you at the cadence you want to ride.

Presets cover the common road and gravel chainrings (50/34, 52/36, 53/39, 48/35, 46/33), cassettes from 11-25 up to 10-46, and wheel circumferences for standard tyre sizes. Your last setup is saved locally so the page comes back the same way each time.

[https://tpjnorton.com/tools/gear-calculator](https://tpjnorton.com/tools/gear-calculator)

## 6. Bouldering Calorie Calculator

![Bouldering Calorie Calculator screenshot](/images/blog/bouldering-calculator.png)

If you climb, you've probably been irritated by the fact that an Apple Watch puts a 90-minute hard bouldering session at the same calorie burn as standing around. The problem is that no consumer wearable knows how to score climbing properly. The activity is mostly rest, the active bursts are short and intense, and the energy cost depends on grade, wall angle, and how much you rest between attempts.

This calculator estimates calorie burn for a session based on:

- Body weight
- V-grade (interpolated MET values from the [2024 Compendium of Physical Activities](https://pmc.ncbi.nlm.nih.gov/articles/PMC10818145/), [Watts et al. 2000](https://pubmed.ncbi.nlm.nih.gov/10834350/), and [Callender et al. 2021](https://pubmed.ncbi.nlm.nih.gov/32813982/))
- Session duration
- Rest fraction, the proportion of session time spent resting between attempts
- Wall angle (slab, vertical, overhang, steep cave)
- Subjective intensity on a 1 to 10 RPE-style scale
- Session type (boulder gym vs. board climbing on a Moonboard / Kilterboard / Tension)
- Sex (Compendium MET values come predominantly from male subjects, so a small correction applies)

The output is a calorie total with a ±20% confidence band, broken down into active calories, EPOC (excess post-exercise oxygen consumption, the after-burn from intense efforts), and rest calories.

[https://tpjnorton.com/tools/bouldering-calories](https://tpjnorton.com/tools/bouldering-calories)

## Getting moving again

All of them came from the same place: a specific question I had while training, no good free answer online. Writing the tool used to take a weekend or two, and now I can get it done in a day or less. Once it's done it stays online for anyone with the same question later.

It feels so great to be able to ship something again, even if it's just a little tool for a niche audience. It's a reminder of why I got into engineering in the first place: to build things that solve problems, even small ones. If you have a question about your training that you wish there was a free tool for, or if you find a bug ,or want to suggest a feature, [let me know](https://tpjnorton.com/contact). Maybe I'll build it next!

Everything lives at [tpjnorton.com/tools](https://tpjnorton.com/tools), all of it free. Go try them out and let me know what you think!
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
        <item>
            <title><![CDATA[Testing My Bike Split Calculator Against Real Data]]></title>
            <link>https://www.tpjnorton.com/blog/posts/testing-my-bike-split-calculator-against-real-data</link>
            <guid>https://www.tpjnorton.com/blog/posts/testing-my-bike-split-calculator-against-real-data</guid>
            <pubDate>Wed, 29 Apr 2026 12:00:00 GMT</pubDate>
            <description><![CDATA[I built a free bike split calculator. Here's what happens when I tested it against four of my own Strava rides, and what the validation showed me to fix next.]]></description>
            <content:encoded><![CDATA[
![header photo](https://images.unsplash.com/photo-1695238070179-218301c2480d?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D)

A few weeks ago, to plan for some races I have this year, I [built a free bike split calculator](/blog/posts/building-a-cycling-power-calculator) that estimates finish time from a GPX route, power targets, and a handful of physics parameters. It's based on a physical model including drag, weight, rolling resistance, elevation, grade etc. It produces real-looking results, but I've been wondering:

> Do its predictions match real rides?

So I pulled four of my own rides, fed each one into the calculator at my actual average power for the day, and compared the predicted finish time to the time I actually rode. To keep things honest I refactored the calculator into a pure function and called the same code from a Node script that parses FIT and GPX files, so the validation runs the same simulation the web page runs.

## The setup

I picked four rides that varied in length and climbing density:

1. A short hilly loop, 32 km
2. Another short hilly loop, 35 km
3. A medium-length climb-heavy ride, 56 km with one long climb
4. A long mountainous ride, 101 km with a 30 km descent

Across all rides I held the parameters constant:

- FTP: 300W (used for IF/TSS context. The prediction is driven by avg power directly.)
- Rider weight: 85-88kg depending on the ride
- Bike weight: 9kg
- CdA: 0.35 m² (my normal hoods position)
- Crr: 0.0035 (modern slicks)
- Drivetrain loss: 2%
- Air density: 1.16 kg/m³ (warm-ish days)
- Headwind: 0 (none reported)

For each ride I asked: at my actual average moving power, what finish time does the calculator predict? Then I compared to actual moving time from Strava (which strips out auto-pause for traffic lights and food stops).

## The four rides

| Ride | Distance | Climbing | Climbing density | Avg power | Moving time | Predicted | Δ |
| --- | --- | --- | --- | --- | --- | --- | --- |
| [Getting the Suffering In](https://www.strava.com/activities/18200826742) | 35 km | +695 m | 20 m/km | 184W | 1:32:41 | 1:31:14 | **−1.6%** |
| [Internal Center Lock Disc Brakes](https://www.strava.com/activities/18229900006) | 32 km | +667 m | 21 m/km | 220W | 1:17:43 | 1:15:03 | **−3.4%** |
| [Monty P and More with the Girlies](https://www.strava.com/activities/18067166303) | 56 km | +1,261 m | 23 m/km | 169W | 2:49:31 | 2:36:47 | **−7.5%** |
| [Here, There and Everywhere](https://www.strava.com/activities/18253328060) | 101 km | +1,472 m | 15 m/km | 175W | 4:13:18 | 3:55:33 | **−7.0%** |

The calculator is consistently optimistic on these rides. Never once did it predict a slower time than I actually rode. The error sits between 1.6% and 7.5%, and it grows with duration.

## What's driving the bias

The model is constant power plus physics. It doesn't see:

- **Cornering on descents.** A 7% descent at my weight and CdA hits ~70 km/h in the model. In real life I'm braking for hairpins, sight-line corners, and other traffic. On the 30 km descent in ride 4 I was averaging 50 km/h. The model expected 70.
- **Stops inside "moving time".** Strava's auto-pause kicks in at near-zero speed, but a 5 km/h crawl through a village still counts as moving and tanks your average. The model has no concept of villages.
- **Fatigue.** A 4-hour effort at 175W avg is hiding the fact that the last hour was probably 165W with the same perceived effort as the first hour at 185W. The calculator assumes you can hold the average forever.
- **Variable pacing helping on hills.** This one actually works in the calculator's favour on hilly rides. Surging on climbs at higher-than-avg power saves more time than the same wattage saves on flats. The 7.5% gap on the [Mont Pèlerin](https://en.wikipedia.org/wiki/Mont_P%C3%A8lerin) would be wider if I'd ridden it perfectly steady.

The first three pulls predictions to be optimistic. The last pushes them to be slightly pessimistic. Add it up and you get the 2-8% optimistic gap I see on real rides.

## The shape of the error

Two patterns from four rides. Small sample, but they line up:

- **Error grows roughly with duration.** A 1.5-hour ride is within ~2-3%. A 4-hour ride lands closer to 7%. Real-world overhead doesn't compound, but it does accumulate. The longer you're out, the more time piles up in cornering, braking, and sitting up.
- **Climbing density adds friction.** The 56 km ride had the highest climbing density (23 m/km) and the worst error (−7.5%). The 101 km ride had only 15 m/km and similar error. More climbs means more descents, and descents are where the model is weakest.

A reasonable heads-up to put on a prediction: expect 2% baseline error, plus ~1-1.5% per hour, plus more if the route is descent-heavy. The number you see on screen is the floor for what's possible if everything goes right. Real rides land slower.

## What I did about it

**1. Results are now a three-point prediction range.** The headline now shows a likely time with optimistic and worst-case below it, instead of a single confident-looking number. The likely figure is the physics prediction plus a real-world overhead that grows with duration: roughly 2% baseline plus ~1.2% per predicted hour. The worst-case adds a larger pad for technical descents and bad days. The race plan, fueling calculator, and target-time check all run off the likely time so the planning numbers stay self-consistent. Section-by-section times in the breakdown table stay as the physics estimate, since pacing power output is what those rows are for.

**2. Cornering is still the unfinished business.** Modelling corner radius from GPX point geometry and capping local speed at `sqrt(μ·g·r)` is the biggest remaining lever for descent realism. It's also the hardest piece, so I'm sequencing it after the easier wins. Once it's in, the gap between optimistic and likely should narrow on technical courses.

## Try it yourself

If you want to validate the calculator against one of your own rides, [it's here](/tools/bike-calculator). Pull up a GPX from a ride where you had power data, enter your actual average power as the climb / flat / descent power, and see how the predicted time compares to what you actually did. If your error is meaningfully outside the 2-8% range I'm seeing, I'd like to hear about it.
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
        <item>
            <title><![CDATA[Using Math to Predict Cycling Performance]]></title>
            <link>https://www.tpjnorton.com/blog/posts/building-a-cycling-power-calculator</link>
            <guid>https://www.tpjnorton.com/blog/posts/building-a-cycling-power-calculator</guid>
            <pubDate>Fri, 10 Apr 2026 12:00:00 GMT</pubDate>
            <description><![CDATA[How I built a free GPX-based bike split calculator to figure out if I can beat last year's 3rd place bike leg at the Geneva Triathlon.]]></description>
            <content:encoded><![CDATA[
![header photo](/images/blog/bike-calculator.png)

On the 4th of July this year, I'm doing the bike leg of a relay sprint triathlon at the [Geneva Triathlon](https://www.genevetriathlon.ch/en/races/short). Looking at last year's results, third place on the bike split was tantalisingly close to what I reckoned I could do in 'peak' shape. So, in a bid to add a dose of reality to my hubris, I figured it would be appropriate to more accurately answer the following questions: _Knowing the course ahead of time, what power do I actually need to hold on which parts of the course to beat that time, and thus, is it actually possible?_

Turns out this isn't a simple calculation. The course has a climb at the start, a long flat section, some rolling terrain, and a couple of moderate descents. How hard you push on the climb versus the flat changes your total time in non-obvious ways. And it all depends on your weight, your bike, your aerodynamic position, the wind, and a dozen other variables.

I couldn't find a free tool that let me upload the GPX, tweak all the physics parameters, and instantly see what happens when I change my power on different sections. The closest thing is [Best Bike Split](https://www.bestbikesplit.com/). It's a paid SaaS at around $19/month, and the free tier doesn't give you course-specific pacing. So I built one.

## The Problem with "Eyeballing It"

It's not so hard for an experienced cyclist to estimate individual sections and the power they can/should hold on them. But when you add it all up, the interactions become complex. For example, if I push 20 watts harder on the climb, I might save 30 seconds there, but that extra effort could cost me 30 seconds on the flat later due to fatigue. Or, if I hold back on the climb to save energy for the flat, I might lose more time on the climb than I gain on the flat. The combinations quickly grow beyond what (at least speaking for my limited self) can fit in one's head.

The only way to know for sure is to model the entire course with all the physics involved.

### How the math works

The fundamental cycling power equation is well-known. Your power output has to overcome four forces: aerodynamic drag, rolling resistance, gravity, and drivetrain friction. At a given power, on a given gradient, there's exactly one steady-state speed that balances the equation.

At steady state, the power equation looks like this:

```
P_effective = P_aero + P_rolling + P_gravity
```

Where:

```
P_effective = P_rider × (1 - drivetrain_loss)

P_aero     = 0.5 × CdA × air_density × v_air² × v_ground
P_rolling  = Crr × m × g × v_ground
P_gravity  = m × g × sin(slope) × v_ground
```

- **P_rider** is your pedalling power (watts)
- **CdA** is your drag coefficient times frontal area (m²) — this is your aerodynamic "signature"
- **v_air** is your speed relative to the air (ground speed + headwind)
- **Crr** is the rolling resistance coefficient of your tyres
- **m** is the total mass of you + bike + kit (kg)
- **g** is gravity (9.81 m/s²)
- **sin(slope)** is derived from the road gradient

The key insight: **v_air** appears as a squared term inside P_aero, but it also depends on **v_ground**, which is what we're solving for. This makes the equation implicit — speed is on both sides. You can't isolate it algebraically.

Instead, the calculator uses [Newton-Raphson iteration](https://en.wikipedia.org/wiki/Newton%27s_method) to find the speed where power in equals power out. Starting from an initial guess, it iteratively refines:

```
v_next = v - f(v) / f'(v)
```

Where `f(v) = P_total(v) - P_effective` is the power residual at speed `v`, and `f'(v)` is its derivative. Within 10-20 iterations, this converges to a solution accurate to 0.001 watts.

By hand? Miserable. In a spreadsheet? Possible but fragile. Being the incidentally lazy person that I am, I wanted a tool where i can just drag sliders around after uploading the GPX, and see the results.

## How It Works

The calculator does a few things:

**1. GPX Parsing & Gradient-Aware Splitting**

When you upload a GPX file, it extracts the lat/lon/elevation data, smooths the elevation to reduce GPS noise, then splits the route into sections based on _actual terrain changes_, not equal distances. This is important. If you split a route into 8 equal chunks, a 7% climb that happens to start halfway through a chunk gets averaged with the surrounding flat into a meaningless "3.5%" section. The gradient-aware algorithm detects where the terrain actually changes and creates sections that reflect reality. This was actually the tricky bit to get right - how should I split a given elevation profile into sections that are "flat enough" vs "climby enough"? I ended up with a heuristic that looks for sustained gradient changes above a certain threshold, while also enforcing a minimum section length to avoid over-splitting.

**2. Physics Simulation**

For each section, given your power, weight, CdA (aerodynamic drag), rolling resistance, and conditions, the Newton-Raphson solver finds the speed. Distance divided by speed gives time. Sum all sections and you have your estimated finish time.

**3. Reverse Solving**

This is the bit I find most useful. Instead of "given this power, what's my time?", you can ask "given this target time, what power do I need?" The tool binary-searches for the wattage that produces exactly your target time. When I set 35 minutes as my goal and uploaded the Geneva course, it told me I needed 247w uniform. That's a very concrete training target.

**4. Per-Section Strategy**

This came from a friend's feedback: "Can I simulate pushing hard on the first climb, recovering on the descent, and pushing again on the flat?" Yes. You can click any power value in the course breakdown and override it. Lock the descent at 200w (recovery), push the climb to 310w, and see how the total time changes. The solver adapts too, telling you what the _remaining_ sections need if some are locked.

## What I Learned About the Physics

A few things surprised me while building this:

**Aerodynamics matter more than power on flat roads.** Going from hoods (CdA ~0.32) to drops (CdA ~0.30) saves more time on a flat 20km course than adding 10 watts. If you're doing a time trial, getting low is literally free speed.

**Extra watts on a steep descent are almost worthless.** At 60+ km/h downhill, you're fighting cubic air resistance. An extra 50 watts might gain you 2 seconds over a 2km descent. Spend those watts on the climb instead, where you're going slow and every watt translates directly to speed.

**The Newton-Raphson solver is surprisingly tricky on descents.** When gravity is assisting you, the power equation flips and the solver can diverge. I had to implement a smarter initial velocity guess and clamp the step size to prevent it oscillating to nonsense values.

**Wind changes everything non-linearly.** A 15 km/h headwind on a flat course costs you far more time than a 15 km/h tailwind saves. This comes from the drag equation:

```
P_aero = 0.5 × CdA × air_density × (v_ground + v_wind)² × v_ground
```

If you're riding at 36 km/h (10 m/s) into a 15 km/h headwind (4.2 m/s), your air speed is 14.2 m/s, making drag proportional to 14.2² = 201. With a tailwind, your air speed drops to 5.8 m/s, making drag proportional to 5.8² = 34. The headwind case has **6x** the aerodynamic drag of the tailwind case. They don't cancel out at all.

## The FTP Helper

Most cyclists know their FTP (Functional Threshold Power), the power they can sustain for about an hour. But how does that translate to race power for different durations?

The tool includes presets based on standard power-duration curves:

- **All-out (<1h):** 105% FTP climbing, 95% flat
- **Hard (1-2h):** 95% climbing, 88% flat
- **Steady (2-4h):** 88% climbing, 82% flat
- **Endurance (4h+):** 80% climbing, 75% flat

Climb power is higher because at low speed there's less aero drag, so more of your effort goes into moving forward. On flats, aero drag dominates and you need to be more conservative to sustain the effort.

Enter your FTP, pick a race intensity, and the sliders set themselves. It's a starting point, and then you can fine-tune.

### The Tech

It's built with Next.js, TypeScript, and Tailwind, running entirely client-side. No server, no accounts, no data leaves your browser. The GPX file is parsed in the browser using the DOMParser API, and all the physics runs in JavaScript.

## How it compares to the paid tools

The obvious comparison is [Best Bike Split](https://www.bestbikesplit.com/). They lead the market for triathlon and time-trial pacing and work with a lot of WorldTour teams. Their pipeline is more sophisticated than mine. Road-surface detection, live weather forecasts, Garmin and Wahoo head-unit integration, an AI race assistant, analytics to compare planned vs. actual power. All polished. All behind a subscription.

The subscription is around $19/month or $119/year. If you race a lot, or if you want your bike computer to beep at you with live power targets on race day, that price is fair.

Here's how mine stacks up.

Shared with BBS: full course-specific physics (CdA, Crr, gradient, air density, drivetrain loss, headwind), GPX upload, goal-time solving, per-section power strategy.

Missing from mine: live weather, road surface detection, head-unit integration, FIT export, multi-plan comparison, race analytics.

One subtle difference worth noting. When you set a target time, my solver picks a single uniform wattage. BBS's optimiser prefers a variable-power plan that pushes harder on climbs and eases on descents. In practice that saves a small amount of time over constant power. You can approximate it here by clicking into the course breakdown and overriding individual sections. Automating it is on my list.

Bottom line: if you're training casually or just trying to answer "can I actually do this?" for a specific course, this tool gets you most of the way there. If you need the full race-day ecosystem with weather and device sync, pay for BBS.

A couple of other free alternatives exist. [sport-calculator.com](https://sport-calculator.com/tools/ironman-bike-split-calculator) has an Ironman-specific version. [TrainerDay](https://trainerday.com/) published a BBS-style calculator aimed at casual riders. Worth knowing they're there.

## Back to the Race

So, can I beat last year's 3rd place? After uploading the Geneva course and plugging in my current FTP, the calculator says I need to average about 250w, holding around 280w on the climb and 240w on the flats. My current FTP puts that right at the edge of "hard but sustainable" for the ~35 minute effort.

Three months of structured training, a clean chain, and a good aero tuck should get me there. Or at least give me a very precise understanding of exactly how far off I am.

Here's hoping I don't blow up on the day!

**[Try the Bike Split Estimator &rarr;](/tools/bike-calculator)**
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
        <item>
            <title><![CDATA[Connecting the SBB to Claude via MCP]]></title>
            <link>https://www.tpjnorton.com/blog/posts/building-a-swiss-transport-mcp-server</link>
            <guid>https://www.tpjnorton.com/blog/posts/building-a-swiss-transport-mcp-server</guid>
            <pubDate>Fri, 27 Mar 2026 12:00:00 GMT</pubDate>
            <description><![CDATA[I built an MCP server that gives Claude access to real-time Swiss public transport data. Here's how it works and what I learned along the way.]]></description>
            <content:encoded><![CDATA[
![header photo](https://images.unsplash.com/photo-1570225131703-61981d8f5cd9?q=80&w=1740&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D)

> **Update (April 2026):** Since publishing this, I've renamed the project from `swiss-transport-mcp` to **`swiss-public-transport-mcp`** to avoid a clash with [an existing server of the same name](https://github.com/malkreide/swiss-transport-mcp) that wraps the `opentransportdata.swiss` APIs. Mine is the one built on `transport.opendata.ch` — different data source, different scope, but the original name was already taken. References below have been updated accordingly.

If you've been anywhere near the AI developer space recently, you've probably heard of MCP. The Model Context Protocol is Anthropic's open standard for giving LLMs access to external tools and data. Think of it as USB-C for AI: a universal way to plug capabilities into models like Claude.

I've been living in Switzerland, and the Swiss public transport system is incredible. Trains run on time (mostly), trams connect everything, and boats cruise across lakes on a schedule. There's a free, open API that exposes all of it at `transport.opendata.ch`. So naturally, I thought: what if Claude could just... use it?

That's how **swiss-public-transport-mcp** was born.

## What It Actually Does

The server exposes four tools to Claude:

- **`search_locations`** finds stations by name or GPS coordinates
- **`plan_journey`** gets connections from A to B, with real-time delays, platforms, and occupancy
- **`get_stationboard`** shows live departure/arrival boards for any station
- **`get_booking_link`** generates a deep link straight to sbb.ch to buy tickets

You can ask Claude "How do I get from Lausanne to Brig this morning?" and it'll give you actual, real-time train options, then hand you a link to book.

Here's what that looks like in practice:

![Journey plan from Lausanne to Brig](/images/blog/journey-plan.png)

## Why Not Just Use the SBB App?

Fair question. The answer is context. When you're mid-conversation with Claude, maybe planning a trip or working out a multi-city itinerary, switching to a separate app breaks the flow. MCP lets the transport data live inside the conversation. Claude can reason about connections, compare options, and factor in things you've already told it. It's the difference between looking something up yourself and having a knowledgeable friend who just knows.

## The Architecture

I wanted this to be more than a weekend hack. The codebase follows a clean layered architecture:

```md
Tools (FastMCP) → Service → Client → Models → Formatters
```

**Models** are plain Python dataclasses. No heavy validation framework where it's not needed. Pydantic only shows up at the tool boundary, where it validates the inputs Claude sends.

The **API client** wraps `transport.opendata.ch` with retry logic using exponential backoff and jitter for rate limits and server errors. It also handles some fun API quirks, like the coordinates being reversed (`x=latitude`, `y=longitude`) and durations coming back in formats like `"00d01:18:00"`.

**Formatters** turn raw data into human-readable text rather than JSON. This is deliberate. LLMs process structured text more naturally than nested JSON, and it makes the output immediately useful in conversation. Delays get a ⚠ symbol, walking segments show 🚶, and occupancy displays as `●●○` so you know which carriages to avoid.

## The Fun Bits

A few things I particularly enjoyed building:

**Deep linking to SBB.** Generating booking URLs sounds simple until you realise SBB's URL format uses underscores instead of colons for times and has its own parameter conventions. Getting this right means Claude can end a journey plan with a link to actually book it, which feels genuinely useful.

**Station ambiguity handling.** Ask for "Basel" and the API returns multiple stations (Basel SBB, Basel Badischer Bahnhof, Basel SNCF...). Rather than failing, the service returns a clarification prompt listing the options so Claude can ask you which one you meant.

**Real-time awareness.** The server doesn't just give you the timetable. It shows predicted times vs. scheduled, computes delay minutes, flags cancellations, and reports per-class occupancy levels. It's the kind of information that makes the difference between catching your connection and watching it leave.

## Getting Started

If you want to try it yourself, clone the repo and add it to your Claude config:

```json
{
  "mcpServers": {
    "swiss-public-transport": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/swiss-public-transport-mcp", "swiss-public-transport-mcp"]
    }
  }
}
```

The whole thing is open source and MIT licensed.

## What I Learned

Building an MCP server is surprisingly satisfying. The protocol is simple enough that you can get something working fast, but there's real depth in making the output good. Raw API data is rarely what an LLM, or a human, actually wants to read. The formatting layer ended up being where most of the thought went: deciding what to show, what to hide, and how to make a timetable scannable in a chat window.

If you've got a favourite API that you wish Claude could access, I'd genuinely encourage you to build an MCP server for it. The barrier to entry is low, and the result feels a bit like magic. Your AI assistant suddenly knowing things it couldn't know before.
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
        <item>
            <title><![CDATA[Building an Electronic Press Kit (EPK) for Your Band]]></title>
            <link>https://www.tpjnorton.com/blog/posts/building-electronic-press-kit</link>
            <guid>https://www.tpjnorton.com/blog/posts/building-electronic-press-kit</guid>
            <pubDate>Sat, 08 Apr 2023 08:54:29 GMT</pubDate>
            <description><![CDATA[In today's digital age, having a strong online presence is crucial for a band's success. One important tool for promoting your music and getting noticed by industry professionals is an electronic press kit (EPK).]]></description>
            <content:encoded><![CDATA[
![header photo](https://images.unsplash.com/photo-1648260295950-29435aa1678a?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=764&q=80)

Photo by [Alphabag](https://unsplash.com/@alphabag) on [Unsplash](https://unsplash.com)

In today's digital age, having a strong online presence is crucial for a band's success. One important tool for promoting your music and getting noticed by industry professionals is an electronic press kit (EPK). An EPK is a collection of digital assets that showcases your band's music, history, and achievements, and it's designed to provide easy access to important information for press, booking agents, and other industry professionals.

As an artist myself, I have my own [EPK](https://tpjnorton.com/music) that I use to promote my music and connect with industry professionals. I've found that having an EPK has helped me land some great opportunities. In this article, I'll share five key elements that every artist should include in their EPK.

## What to put in your EPK

Here are five key elements to include in your band's EPK:

### 1. Music Samples

The most important part of any EPK is the music. Include a selection of your band's best tracks, along with short descriptions of each song. Make sure to choose songs that best represent your band's style and sound. You can also include links to music videos, live performances, or recordings of radio appearances.

### 2. Biography and Press Clippings

Your band's biography should provide an overview of your musical history, influences, and achievements. Include details about any major performances or awards, and be sure to highlight any press coverage you've received. Press clippings can include reviews of your music, interviews with band members, and feature articles in local or national publications. Remember to keep your bio and press clippings up-to-date, and consider hiring a professional writer to help you craft a compelling story that will capture the attention of industry professionals.

### 3. High-Quality Photos and Videos

In addition to music and written content, your EPK should also include high-quality photos and videos of your band. These can include professional photos of band members, live performance footage, or music videos. Make sure all media assets are of high quality and clearly showcase your band's style and personality. Consider investing in a professional photo or video shoot to ensure that your visuals are top-notch.

### 4. Contact Information

Make it easy for industry professionals to get in touch with your band by including clear and up-to-date contact information in your EPK. This should include email addresses, phone numbers, and social media handles. Consider creating a separate email address specifically for EPK inquiries, and make sure to respond promptly to any requests for more information.

### 5. Tour Dates and Upcoming Shows

Finally, be sure to include a list of upcoming tour dates and shows in your EPK. This will help industry professionals understand your band's schedule and availability, and it can also help you book new gigs. Include information about past shows as well, highlighting any notable performances or venues you've played.

## Building your EPK

When it comes to building an electronic press kit (EPK) for your band, there are a variety of services available that can help you create a professional-looking and effective EPK. Two of the most popular services for building EPKs are Bandzoogle and ReverbNation. Here's a comparison of these two services, as well as a few other options:

### Bandzoogle

[Bandzoogle](https://bandzoogle.com) is a website builder designed specifically for musicians. In addition to creating an EPK, you can also use Bandzoogle to create a custom band website, sell music and merchandise, and connect with fans. Bandzoogle offers a variety of templates to choose from, and the drag-and-drop interface makes it easy to customize your EPK. Some of the features of Bandzoogle's EPK builder include:

- Music player for showcasing your tracks
- Option to add press clippings, photos, and videos
- Customizable biography section
- Contact form for industry professionals
- Option to embed your EPK on your website or share a link directly

Bandzoogle's pricing starts at $8.29/month for the basic plan, which includes unlimited pages, hosting, and custom domain name.

### ReverbNation

[ReverbNation](https://www.reverbnation.com) is another popular platform for musicians, offering a variety of tools for promoting and selling your music. In addition to creating an EPK, you can use ReverbNation to distribute your music, book shows, and connect with fans. [ReverbNation's EPK builder](https://www.reverbnation.com/band-promotion/press_kit) offers the following features:

- Music player for showcasing your tracks
- Option to add press clippings, photos, and videos
- Customizable biography section
- Contact form for industry professionals
- Option to embed your EPK on your website or share a link directly
- EPK analytics to track views and engagement

ReverbNation offers both free and paid plans, with the paid plans starting at $19.95/month for the Pro plan, which includes additional promotional tools and distribution options.

### Wix/Squarespace

Alternatively, if all else fails, a regular website builder can be used to create your EPK. [Wix](https://www.wix.com/) is a popular website builder that can be used to create an EPK as well as a custom band website. With Wix, you can choose from a variety of templates and customize your EPK with a drag-and-drop interface. Some of the features of Wix's EPK builder include:

- Music player for showcasing your tracks
- Option to add press clippings, photos, and videos
- Customizable biography section
- Contact form for industry professionals
- Option to embed your EPK on your website or share a link directly

Wix offers a free plan as well as a variety of paid plans starting at $14/month.

Overall, each of these services offers a variety of tools for creating a professional-looking and effective EPK. When choosing a platform, consider the features that are most important to you, as well as the pricing and ease of use.

### Wrapping Up

In conclusion, an electronic press kit is an essential tool for any band looking to promote their music and reach a wider audience. By including key elements like music samples, a compelling biography, high-quality photos and videos, contact information, and tour dates, your EPK can help you stand out in a crowded music industry and make meaningful connections with industry professionals. There are many tools out there to build your EPK, and each of these services offers a variety of features to help you create a professional-looking and effective EPK, although the best aren't free.
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
        <item>
            <title><![CDATA[Awesome Next.js API Routes with next-api-decorators]]></title>
            <link>https://www.tpjnorton.com/blog/posts/awesome-next-js-api-routes-with-next-api-decorators</link>
            <guid>https://www.tpjnorton.com/blog/posts/awesome-next-js-api-routes-with-next-api-decorators</guid>
            <pubDate>Tue, 09 Nov 2021 17:31:10 GMT</pubDate>
            <description><![CDATA[Building a modern Web application can be a complex task - it's likely that if you're looking to build something you'll start thinking about…]]></description>
            <content:encoded><![CDATA[
![photo](https://cdn-images-1.medium.com/max/800/0*f9UejiBr8bajU3SI)

Photo by [Matt Duncan](https://unsplash.com/@foxxmd) on [Unsplash](https://unsplash.com)

Building a modern Web application can be a complex task - it's likely that if you're looking to build something you'll start thinking about architecture, and how to separate your concerns, and that there are many ways that can be done. Traditionally speaking, this means having a separate frontend and backend, with separate codebases, maybe using different languages, frameworks, or technologies in order to get the job done.

In this article, we'll look at an alternative using something wonderful from the world of frontend development: Next.js. We'll look at how a monolithic architecture can be convenient and powerful, without necessarily suffering from the tight coupling usually linked with this approach. But first, a quick primer on how Next.js works.

### How Next Works

If you're new to this tech, Next.js is a Jamstack framework, designed to solve some of the problems associated with building traditional Single Page Apps (SPAs). Not only does it support Server-Side Rendering, but also Static Site Generation, allowing developers to build apps with some static pages (think marketing / about pages!) and some dynamic pages that load super quick and are SEO-friendly. Depending on your use case, you can usually get Next.js to suit your needs, with very few caveats.

To put it briefly, this works because when you "run" a Next.js app, you're actually running a Node.js server (though if you're building a completely static site, you can just export all the static files and host them wherever, but we won't be talking about that approach here). As a result, your app is 'alive', in contrast with e.g. a Create React App, where a JS bundle is hosted somewhere and downloaded to the client where the whole app is rendered only on the client side.

So what?

As a result of this, it's possible to define some code that only ever runs on the server, that the client will never see. It's starting to look like given that we have server-side-only code, we could maybe start doing some stuff that we couldn't (more like _shouldn't_) do before on the client, e.g. talk to a database directly, perform operations that require several separate calls, etc. Isn't this starting to sound like an HTTP API?

Next.js supports this concept as a first-class citizen in its architecture. Routing is opinionated, and uses the filesystem to determine page routes in your app, using files and folders in a directory called pages to establish this - e.g. if you create a file in `pages/todos` called `mine.tsx` you'd be able to find the page in your browser at `<your-domain>/todos/mine` . Anything under the path `pages/api` will only run on the server. There we have it! What we refer to as [Next API Routes](https://nextjs.org/docs/api-routes/introduction). This now means we can build a full-stack application with a single codebase, a "monolithic" architecture. Right off the bat we get several nice-to-haves for free:

- Single build process & easy-to-run dev environment for whole app (just run `next dev` )
- Simpler deployments ( `next build` then `next start` in any node-compatible environment)
- No CORS worries by default, the frontend and backend are on the same origin!
- Monorepo-like sharing by default (think same `tsconfig`, `jest`, `eslint` ) without having to use monorepo managers like `lerna`

More experienced developers may see an approach like this as a step backwards, but it's arguable more like a step towards better consolidation of code & resources. Logical concerns can be separated without physical separation itself, and a monolithic architecture is beginning to make more sense for Web apps, most notably with the rise of Incremental Static Regeneration and Server Side Rendering.

#### API Routes

As listed in the documentation, building API routes with Next looks a lot like building a basic API with Node.js:

`gist:tpjnorton/f4d0e18d77ef78f4280dd7abae783d75`

Nice and simple. You can use this approach to very quickly and simply build out some server-side functionality!

But what if we wanted to start building a REST API, differentiating between different HTTP methods on the same endpoint, modelling our API in terms of resources? What if we wanted to start composing behavior into our API endpoints?

Our example quickly swells to something like this:

`gist:tpjnorton/ac814805485f8bb2a2652425eefd0f1c`

And that's probably the best case, as we've been able to factor out all the actual business logic into the functions `doSomething`, `checkAuth`, `doSomethingElse` and `doAThirdThing`. It'd likely get worse once we'd start getting request bodies etc. Not terrible, but we can likely make this a little easier to read, and a little easier to maintain.

### NestJS

Often, developers opt for a framework when building out APIs, as they offer alternative means of representing resources, and many tools and utilities designed to make this process easier. One such example is [NestJS](https://nestjs.com) (Not to be confused with Next.js!). This is a batteries-included, powerful "framework for building efficient, scalable [Node.js](https://nodejs.org) server-side applications". Heavily inspired by [Angular](https://angular.io), this framework allows developers to use abstractions to represent API routes, including reusable services, controllers, modules and more.

Here is a simple example copied from its documentation:

`gist:tpjnorton/36ef6276e6bf15b27dad5246784992bb`

As we can see, this approach uses OOP ideas and decorators to achieve the goals described previously. We specify only the HTTP methods we intend to make available as methods on a class, instead of control flow paths in a function. A little more declarative, and a great foundation if we're looking build out a REST API.

So how do we make use of this approach for Next.JS API Routes?

### next-api-decorators

This library, written by the wonderful folks at [Story of AMS](https://storyofams.com), an eCommerce tech agency based in Amsterdam, decided to develop a solution using these concepts to make building an API easier to scale with Next.js.
Let's revisit our earlier complex example rewritten using their library to demonstrate how it works:

`gist:tpjnorton/2d5f2288df65c6211e6ce4a129b15b7c`

As we can see, we have changed the way we express our resource handler, swapping a function for a class. On top of this, methods of the class are annotated with decorators to express their purpose.

Now, it's up to you which you prefer, as it's not as if this approach makes it much more concise. But it does, as mentioned earlier, transform our approach to describing our resources from an imperative one to a declarative one. I have found while building Next.js APIs out that this approach is a good deal easier to maintain, as eventually each route and each handler **can** and **will** get more complex as your application grows.

#### Custom Decorators

The last thing I wanted to look at from this example is the `requiresAuth` decorator we see on some of the methods in this handler now. I wrote something like this custom handler for a project recently, and it's proven very useful. This is done using the `createMiddlewareDecorator` utility provided by `next-api-decorators`:

`gist:tpjnorton/5411d6fdc26d6da787df1d90a7ca9c2d`

Pretty strong stuff 💪

This approach is very flexible and can be applied to both classes and their methods to fit a variety of use cases and declutter your actual business logic.

### Wrapping Up

The example I've shown here is very simple and just to give you an idea of the a practical monolithic approach to building a full-stack application using Next.js and its API routes, and how it can be made scalable server-side using `next-api-decorators`.

If you're interested in learning more, check out the [documentation](https://next-api-decorators.vercel.app/) and [repo](https://github.com/storyofams/next-api-decorators) for this amazing library.

Thanks for reading!

💙
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
        <item>
            <title><![CDATA[Next.js 12 Performance Test: Builds Appear to be Slower, Not Faster]]></title>
            <link>https://www.tpjnorton.com/blog/posts/next-js-12-performance-test-builds-appear-to-be-slower-not-faster</link>
            <guid>https://www.tpjnorton.com/blog/posts/next-js-12-performance-test-builds-appear-to-be-slower-not-faster</guid>
            <pubDate>Mon, 01 Nov 2021 13:31:10 GMT</pubDate>
            <description><![CDATA[This week, Vercel announced the release of Next.js 12 at Next.js conf, surprising everyone, given the relatively short time span since the…]]></description>
            <content:encoded><![CDATA[
![photo](https://miro.medium.com/max/700/0*Me0foaEXIIJ_-r2z)

Photo by [Dynamic Wang](https://unsplash.com/@dynamicwang) on [Unsplash](https://unsplash.com)

This week, Vercel [announced the release of Next.js 12](https://nextjs.org/blog/next-12) at [Next.js conf](https://nextjs.org/conf), surprising everyone, given the relatively short time span since the release of version 11 in June of this year.

As a framework, Next.js aims to provide a complete solution for building Jamstack apps to fit most (if not all) use cases. It’s a bit like writing a React app without having to compromise on SEO, download several MB of JavaScript to load any page, etc. Mind you, this does come with its own caveats.

As part of the announcement, Vercel unveiled a whole host of exciting new features. These new features include a native compiler written in Rust (usurping Babel!), middleware support, URL imports (like [Deno](https://deno.land/)!), React 18 and server component API support, and much more.

Instead of going through all these amazing new things, as many have done already, I wanted to take an in-depth look at the most wide-reaching difference-maker in this update: the Rust compiler. Every app built with Next.js will make use of this, so it’s important to understand what real-life impact this will have.

## Feeling a little Rusty

The new Rust compiler is reportedly built on [swc](https://swc.rs/), the ‘speedy web compiler’, a Rust-based JavaScript/TypeScript compiler designed to offer native-level performance, to be integrated into web toolchains. The big impact of this is replacing Babel, one of the most battle-tested transpilers, used in most modern web apps, certainly any that make use of modern JS features, in order to provide compatibility with as many browsers as possible. It’ll be interesting to see where the Babel project goes, as more native-code-driven toolchains get built out.

Below is a performance comparison, sourced from the release announcement, found [here](https://nextjs.org/blog/next-12#faster-builds-and-fast-refresh-with-rust-compiler):

![performance comparison](https://miro.medium.com/max/700/0*FQZa1odmfY2Hw_OP)

Now, this is a release announcement, not a technical paper, so I’m not going to criticize that data presentation. It’s certainly impressive looking, but lacking substance and depth. It’s certainly not an indicator of real-world mileage making this switch. I can understand engineering managers across the industry having doubts about moving to a new build stack from something as mature as Babel, especially if they don’t have in-depth data better indicating the real-world return on investment (more the reward for the risk of upgrading now).

## Testing Strategy

I wanted to provide a proper build performance comparison for real-world use cases, alongside a “control”, so I picked 3 test cases, testing between Next.js 11.1 and Next.js 12:

- A brand new `create-next-app` app, with a very simple homepage (Our control).
- A static website using SSG and ISR, featuring images, blog posts etc. (Actually my website, [tpjnorton.com](https://tpjnorton.com)).
- A complex web application using SSR, Next API routes etc.

These three examples were chosen to provide meaningful numbers for some common use cases.

### Methodology

It’s easy to meaningfully measure build time improvements as they’re well-reported (just out of the build) and easy to reproduce. However, getting data for fast refresh is hard to do precisely, and obtaining results in a way that really communicates improvements objectively is harder still. So, for the purposes of good science, I’m going to actually only collect and report build data. I’ll also clarify that all these apps use TypeScript. I’ve disabled `eslint` during builds to focus more on actual build performance, and measure both initial (no `.next` directory) and incremental build times. Oh, and I’m on Windows 10.

Let’s get going!

### Case 1: Starter App

First, we create a brand new next app using the following command:

```bash
yarn create next-app --typescript
```

This gets us set up with a very basic project, using Next 12. Then, we can get a Next 11 version by doing the same and altering dependencies to get the same starter with Next.js 11.

### Case 2: Static Website

The tests were performed using an existing Next.js 11.1 app first, then upgrading the site to Next.js 12, and re-running the same initial and incremental build tests.

### Case 3: Complex SSR App

I took the same approach as for the static website for this. First initial and incremental build times were measured using Next.js 11, then the same again on Next.js 12.

## Results

Oh boy, you’re not going to like this.

Here is a chart showing my results. Columns are grouped by the type of build, and each color is for each different use case (Google Sheet [here](https://docs.google.com/spreadsheets/d/1WcFkOcuOMAEzSnaVSjkIu4ypZaHu-JDYaRIuAioLfws/edit?usp=sharing)):

![results chart](https://miro.medium.com/max/700/0*2oNphH76agFvSq4o)

With the exception of the starter app, all the builds with Next.js 12 are slower than with Next 11. I’m extremely surprised. For the starter app, I could understand there being a **slower** build process, as there’s a lot of non-code-compiling work to do to complete a `next build`. However, as we move across the board the static app to the SSR app, which is also a move to apps with increasing amounts of code (the static app is small-mid size, and the SSR app is large), this overhead should diminish comparatively speaking, and we should be seeing some healthy performance gains with `swc`.

At least, that’s what I would expect.

I think it’s difficult to make any real guesses about why this might be, and at best, that’s all they’ll be for me, for now, guesses. My instinct tells me that `swc` is much faster, but there’s something around how the new toolchain is invoked/used that’s causing the in-practice slowdown for me. Maybe it’s not been tested on Windows much yet — it’s hard to be sure.

### Bad Science

It’s important to clarify that my experiments are not very rigorous. I wanted mostly to get a ballpark figure for how much faster builds would be, as I was extremely excited by the news of this new version. As always, software projects (especially Next.js) constantly evolve, and while major changes introduce kinks as seen here, they are often resolved very quickly. I also want to clarify that I have a personal bias with this too — I’m a huge fan of Next.js and have used it for both new and ported existing projects to use it, and this move to native toolchains, while yet to bear fruit for me, is a brave and powerful innovation in the world of the web. So I’m keen to cut them some slack where I can 😄.

## Wrapping Up

I think it’s very easy to jump on something new like this and write a half-baked hit-piece for clicks about how rubbish something is because “it didn’t work well for you”. That’s not what I’m trying to do here. Though I acknowledge these results appear controversial, I feel it’s important to share them, as it teaches a meaningful lesson to developers and managers alike about adopting shiny and exciting new tech — it’s often best to wait a bit first.

Thanks for reading!

💙
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
        <item>
            <title><![CDATA[If It Isn’t Already, Your Startup’s React Project Should Be Written in TypeScript]]></title>
            <link>https://www.tpjnorton.com/blog/posts/if-it-isnt-already-your-startups-react-project</link>
            <guid>https://www.tpjnorton.com/blog/posts/if-it-isnt-already-your-startups-react-project</guid>
            <pubDate>Wed, 25 Nov 2020 07:02:47 GMT</pubDate>
            <description><![CDATA[I used to object to the idea of TypeScript. To me, it didn’t seem to make sense to add static typing to a language...]]></description>
            <content:encoded><![CDATA[
![header photo](https://miro.medium.com/max/700/0*Nqghga5oLWWMZXkl)

Photo by [Deon Black](https://unsplash.com/@deonblack) on [Unsplash](https://unsplash.com)

I used to object to the idea of TypeScript. To me, it didn’t seem to make sense to add static typing to a language that was designed not to have it. I started my career as a C++ Graphics Engineer, before transitioning to being a Front-End Designer and Developer. So static typing wasn’t anything I wasn’t already familiar with.

Moving to JavaScript showed me how fast and easy it was to express various forms of data manipulation, especially with newer versions of JavaScript (ES6 onwards), and it seemed like working, rich applications could be built lightning fast. I felt like a weight had been lifted, being in a world without static typing, and began to feel like static typing would become a thing of the past for me and anyone else in the startup world.

Moreover, at the time tooling was split between communities, there were people asking [why they couldn’t just write C# for the web instead](https://github.com/microsoft/TypeScript/issues/8893), and support for most modern frameworks was pretty limited.

One of the biggest questions a fledgling startup has to answer when considering any kind of tech stack change is: how long will it take? You want to be releasing changes quickly and easily, and you definitely don’t want a holistic port to create big bubbles in your release pipelines. Additionally, you want to consider the implications of such a change on your DX (Developer Experience) and also the speed at which you get things done. We all know that as codebases mature and swell, dev speed inevitably slows down. So you’re looking to avoid introducing something that’ll massively swell your codebase, increase your test burden (we’ll talk more about this later) and give your dev team(s) a worse quality of life, work-wise.

So let’s talk about how moving to TypeScript for your React project fits nicely avoids those pitfalls, and can add a lot of value.

### You Can Port Existing Code Incrementally

TypeScript is a superset of JavaScript, meaning in principle any valid JavaScript is valid TypeScript. This means that moving from JS to TS is technically as simple as a config change. In practice, you’ll want to do more than that to really enjoy the benefits of TS. The main point here is that you can easily create a porting policy that lets you migrate over with **zero development bubbles**. It looks a little something like this:

1. Any new code should be written in TypeScript.
2. If a change is made to a JS file, port it to TypeScript as you go past.

What I never really realized is that TypeScript isn’t about entirely static typing, it uses a _progressive_ typing system — that is to say the compiler comes with a very strong type inference system, which in general means you can use as **much or as little typing as you like**. For the most part, the inference engine is strong enough to make the right call in so many cases that you won’t need to annotate lots of your code, while still getting the type safety you want. This means a lot of porting JS to TS is effectively trivial; at a minimum, type annotations will need to be added anywhere it’s not possible for the compiler to infer a type, and this usually means annotating a couple of function signatures and not much more.

Developers can easily write new code in TypeScript, as this incurs zero porting cost (obviously), and porting existing code over is generally extremely straightforward due to the power of type inference.

I have ported many React codebases, both large and small over to TypeScript, and I’ve been able to do so incrementally **every single time**, immediately enjoying its benefits without any kind of pipeline bubble.

One thing one often hears is the ‘increased boilerplate’ that any static type system may introduce.

Consider the following example, a React component in JS, following ‘best practices’:

`gist:tpjnorton/36ae3ee8cafa5357ef2dc6747989a123`

Now in TypeScript:

`gist:tpjnorton/528fedaf10dfe0e952531b4c27b608e7`

Not too different size-wise In fact, our TS example is actually shorter.

Generally speaking, TS with React incurs no more boilerplate (sometimes even less) than using plain JS.

### You can Still Move Fast

As mentioned previously, as a young startup you want to move as fast as possible. You want to delay code bloat and keep your dev speed fast and your release cycle tight. You want to be able to react to changes in priority easily, and a great way to do that is a simple, elegant codebase that requires low effort to make a simple change (Believe it or not, this is not the case for many large codebases, where seemingly meaningless changes take _forever_.). There inevitably comes a point where your development speed slows down, as your code swells, and your test quantity grows. It’s therefore extremely desirable to try and push that slowdown as far back as possible. One of the ways we can do this is to write fewer tests.

Before I continue, I want to say this: **there is no substitute for good tests**. It’s very hard to be completely confident in a software product if it doesn’t have good tests. Each release should be simple and you should be confident that it ‘works’. The thing is, in reality, a lot of tests that get written **aren’t good tests**. Lots of unit tests you find in production codebases either:

1. Test implementation details so they break whenever you change _anything_, or
2. Are just glorified type-checkers, verifying the types of inputs and outputs for the code they’re testing.

Now, there’s no easy remedy for the former. If you have got into the situation, you’ll have to dig yourself out by rewriting these tests. For the latter, TypeScript can replace these tests very easily. You can dump a whole bunch of now-superfluous code, without sacrificing the confidence you’ll have in your next release. Type checking in this way is like a suite of little tests that **run on every save, as you write the code**. While this is not a substitute for behavioral tests, and higher-level approaches like integration and end-to-end tests, an entire class of bugs is de-facto removed from your codebases. Incorrect prop names, case errors etc. all disappear as they’ll never be allowed into your production branch. Couple this with the added ‘implicit documentation’ that static types are often said to bring, and you can be so much more confident about shipping new code.

If you want to move fast, having a small test burden is essential. If you want to be sure about what you’re releasing, good quality code is essential. You’ll have to strike the balance yourself, but TypeScript can be a good way to find a great compromise here.

### The Tooling is Now Just as Good (if not better)

As a developer, working on just about anything is a breeze if you’ve got the right tools. Fortunately, TypeScript is a great example of good tooling!

#### Tooling Troubles

It used to be that in order to adopt TypeScript in an existing React project, it was not easy to set up, and you had to give up using some tools. For example, in the past, you couldn’t use ESLint for TypeScript codebases. A TS-targeting linter, TSLint, was your only choice. TSLint, while still a great project, was way short of the maturity, power and ecosystem of ESLint. So immediately you had to compromise tooling to get types. For me, this was never a fair trade and it was a big argument against migration. Nowadays, this isn’t a problem, as Babel (a widely used JS transpiler) supports TypeScript, meaning you can use tools like ESLint for your TypeScript projects easily. No more compromising on tools.

#### Libraries Have Rich Support

Most (if not all) large JS libraries support TypeScript. Many of them have been written using TypeScript itself, or the community has lovingly gone through the module and written type declarations for it, meaning you can plug your TypeScript codebase into it and get type support for other people’s code as well as your own. It makes it much harder to misuse libraries this way, and avoids lots of time spent head-scratching because you’ve misspelt something.

#### Editor Support

One of the most important tools is the editor they use. If your editor can automate a lot of simple tasks, tell you when you’re getting things wrong and help you with meaningful suggestions a lot of ancillary tasks get accelerated to light speed, and with fewer errors. Most of programming is these ancillary tasks, so speeding them up has a tangible business impact, as you’ll get good stuff done faster without a drop in quality. Visual Studio Code is a very popular text editor because it includes out-of-the-box support for TypeScript, along with great addons that make it even better. Without these tools I know I’d have never met many tight deadlines in the past.
TS’ great ecosystem, tooling and editor support make it one of the most pleasant languages to write, and my level of confidence in what I produce is a world away from the equivalent JS.

### Wrapping Up

In this article I hope to have shown you the incredible value-add you get from using TypeScript in your startup’s tech stack, and just how easy it is to reap the benefits with very few (if any) caveats!

Thanks for reading!

💙
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
        <item>
            <title><![CDATA[5 Lessons I Learned While Releasing A Mobile App]]></title>
            <link>https://www.tpjnorton.com/blog/posts/5-lessons-from-releasing-a-mobile-app</link>
            <guid>https://www.tpjnorton.com/blog/posts/5-lessons-from-releasing-a-mobile-app</guid>
            <pubDate>Mon, 26 Oct 2020 08:54:29 GMT</pubDate>
            <description><![CDATA[This year I wrote and released Still, a mobile app for iOS and Android. Still is an app designed to help people with Anxiety and OCD relax…]]></description>
            <content:encoded><![CDATA[
![header photo](https://miro.medium.com/max/800/0*6N6g9ncVPXTH_1f3)

Photo by [Yura Fresh](https://unsplash.com/@mr_fresh?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

This year I wrote and released [Still](https://stillapp.ch), a mobile app for [iOS](https://apps.apple.com/ch/app/still-anxiety-relief/id1533251796) and [Android](https://play.google.com/store/apps/details?id=com.tpjnorton.still). Still is an app designed to help people with Anxiety and OCD relax. I thought of the idea for during the first COVID-19 lockdown of 2020, as I found myself struggling with Anxiety and figured I could make the tools I needed to help manage it, and maybe I could help others too. I built it out in late August 2020, and released it onto stores in mid-September.

Now before I get into the details of what exactly I learned, I think it’s important to preface this article with a little note:

I’m an indie developer. I work on my projects solo, with a personal budget for everything. As a company with, say, investment capital, or a large budget for your app’s release, you may find some of these things won’t line up with your experience.

On with the article! Here are 5 things I learned building Still.

### 1\. It Takes Courage

In my spare time, [I make music](https://open.spotify.com/artist/4cEtc4SN2eJFPVGvNsbGTH). Whenever you’re showing anyone anything remotely creative, it’s always possible to feel a little insecure about what you’ve done, especially if you you’ve invested emotional energy in the project. It’s never easy to share one’s intimate thoughts and feelings, no matter the medium.

I did all the design for Still, and the subject matter (Anxiety) is close to home for me. Building this mobile app after a while started to feel like I was announcing that to the world, and that’s daunting. It’s not a guarantee that people will like what you’ve done — one always fears the ‘I think it’s good, so it must be good’ delusion.

**_“I think it’s good, so it must be good”_**

It’s important to make the distinction with software, however. What you’re building isn’t a pure expression of creativity ( although in some cases, it may be much closer to that than otherwise), but a product that people are going to use.

You must be prepared to distance yourself from self-doubt, and an emotional attachment to what you create. This is extremely important —otherwise it’s all too easy to start correlating the success of your product with your notions of self-worth. On top of this, as we’ll cover later, release day isn’t your only shot at perfection & success.

### 2\. There Are Hidden Costs Everywhere

There are obvious up-front costs associated with releasing a mobile app.

First of all, there is your development cost. Depending on whether you’re outsourcing your design or development (or both), you’re looking at a serious chunk of money to create anything non-trivial, and add a bit more if you want it done well.

I like to build stuff myself, but I’m a freelancer. In order to understand how much the project is costing me, I track my hours and calculate the total based on my usual hourly rate for clients. Now obviously this is not always 100% meaningful — thus far, I have worked on Still around my client projects and thus it doesn’t translate to missed freelancing revenue. This may not be the case for everyone, and I think it’s a worthwhile exercise to understand how much your time costs, especially for freelancers. Agency owners branching into home-grown products may find this helpful too, as it’s key to understand where you’re spending your money.

So now that you’ve built your app and you’re ready to get it onto stores, here come the hidden costs. For an individual, these are non-negligible! Here are a few of them (in USD):

- Apple Developer membership **\$99/year** (for non-enterprise clients)
- Google Play Store account **\$25** (one time)
- Privacy Policy: **\$0–1000 (**depending on how personalized you need it)
- Domain name (Privacy Policy’s gotta live somewhere!): **\$5–30 / month**
- Hosting costs for app website (depends on your use case & scale): **likely ~\$0–10/ month**
- Total (first year): **\$184–1604!**

And that’s ignoring any costs you may incur for a backend, this is just for the app! For apps with a backend, you can easily stray into the hundreds or thousands if you’re operating at large scale — and remember, if you’re not the one building it, you probably won’t get it for free!

### 3\. It Can Be Tedious

Once you’ve cut your release version and built your binaries, it’s not just ‘ship it’ from there, no matter how excited you are. There are many more steps that go into releasing an app for the first time. Here are a few:

- Screenshots (for every language your app supports!)
- App Store listing metadata (also for every language)
- Privacy Policy
- Legal Data / Compliance
- Getting ready for app review
- Payment info etc.

It’s long-winded, easy to get wrong (if you submit for review with incorrect metadata, the binary could be permanently rejected and you’ll have to submit another) and daunting to newcomers.

That said, there are mechanisms to improve some of this. [Fastlane](https://fastlane.tools/) is collection of open-source app automation tools designed to remove a lot of the manual work associated with shipping a new mobile app for both iOS and Android. You can, for example, use it to automate screenshots for every language, for a bunch of devices, removing a long and painful part of shipping a new binary.

Still — factor in a considerable few hours-days from building your first binary to being ready even to submit for review.

On top of that, app review can take a while. Apple aim to review 90% of submissions within 48 hours, and they were very quick for me. Google Play, on the other hand, took a considerable amount longer, nearly a week. One can chalk this up to the fact that one is paying more for Apple developer membership, thus should expect higher quality service, coupled with the Google Play store receiving more submissions than Apple.

### 4\. Timing Isn’t Everything

Back in the dotcom days, people used to rave about _First Mover Advantage_. Being the first to get your shiny new product (that used sleek, modern acronyms like HTTP and HTML!) in the hands of users was considered a huge advantage over competitors, and was a staple of any investment prospectus of the time.

**_“We’ve got a serious FMA, and we’re generating loooads of buzz.”_**

Not so anymore. These days, most new products are likely to have competitors already, and are looking either to innovate or just grab some market share. This changes the game completely. **You don’t have to be first anymore, instead you need have the best product over the long term.** Either that, or have the best marketing, see [VHS vs BetaMax](https://en.wikipedia.org/wiki/Videotape_format_war)!

I like to think about software with the following analogy. Writing software is a bit like building a house you’ll never finish. There’ll always be an extra window you can put in, some nicer tiles for the roof, new kitchen counters, a patio, an extension, etc. In short, software is never “finished”.

While this sounds initially a little depressing, it does actually imply something amazing! My principal conclusion is: **Things can always improve.**

Just because your product is missing something now, it doesn’t mean that it can’t be there in a day, a week, a month or more. Consider splitting out your book of features over time, and get something out early. Better to set the bar lower, and ship great updates, frequently. The more you focus on this strategy, the sooner you can get your product out in the hands of users, and growing.

On top of that, it makes you much more reactive if something goes wrong, as it’ll be easier to ship a fix if you’re used to doing it (and you’ve set up great CI/CD tools). Just make sure whatever you ship remains high quality, and you never ship anything obviously unfinished.

**_Writing software is a bit like building a house you’ll never finish_**_._

The tl;dr here is that you don’t only have one shot. You can get your product out and ship extra features, improvements and fixes incrementally. What I call “pre-release fatigue” is a real thing, and can be costly even in the short term.

### 5\. Users (Probably) Don’t Come for Free

It’s not 2009 anymore. You can’t just drop your app on the App Store or Play Store for 99 cents and overnight become a huge success. Leading app stores, according to [statista.com](https://www.statista.com/statistics/276623/number-of-apps-available-in-leading-app-stores/), have millions of available apps. Every one of these is something you’re fighting with for mobile users’ attention. You’ve got to be creative, or a marketing genius to stand out against the competition.

Indeed, there are “cost-free” ways of spreading the news about your app. Content aggregation sites like Reddit, social media marketing, even word of mouth can be extremely effective. But the reality is that **they aren’t cost-free.** Remember earlier, when we talked about time investment as an implied cost? Guess what — all of those strategies demand a large amount of effort, or at the very least a considerable time investment in order to produce results. Sure, if you have that time, great. But if you’re like me, that’s harder to accomplish if you’re only just finding time for development of the product itself.

Alternatively, if you’re unable or unwilling to try the above strategies, there’s always traditional advertising. It’s expensive, but it works. Nothing gets people engaged like your app appearing at the top of their searches on app stores, in ads in other apps and in their Instagram feed. But rather than incurring an implied cost, you’ll directly be forking over those $$.

### Wrapping Up

It was a long and interesting journey releasing my first mobile app. I’m continuously working to improve Still and provide great tools to help anxious people. If you’re interested in learning more about Still, [here’s a link.](https://stillapp.ch)

In any case, I hope this article helps you to avoid the pitfalls, understand the cost implications of your release goals, and have an easier time understanding how to get some users!

Thanks for reading!

💙
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
        <item>
            <title><![CDATA[Useful Custom Hooks for Tired React Devs]]></title>
            <link>https://www.tpjnorton.com/blog/posts/useful-custom-hooks-for-tired-react-developers</link>
            <guid>https://www.tpjnorton.com/blog/posts/useful-custom-hooks-for-tired-react-developers</guid>
            <pubDate>Sun, 25 Oct 2020 07:31:10 GMT</pubDate>
            <description><![CDATA[React hooks are awesome. By abusing the power of JavaScript closures and with a few caveats, it’s possible to hold state in a function…]]></description>
            <content:encoded><![CDATA[
![photo](https://cdn-images-1.medium.com/max/800/0*oZtyqEyaGsGbhgrT)

Photo by [Anne Nygård](https://unsplash.com/@polarmermaid?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

Writing React applications can get same-y. We often find ourselves going over the same patterns of defining the same component behavior over and over, and one can start to wonder how we can make these patterns more and more DRY.

React Hooks are awesome. By abusing the power of JavaScript closures and with a few caveats, it’s possible to hold state in a function. How cool is that?

_(Before I get shouted down by C developers who’ll tell me a_ `_static_` _variable in a function is effectively stateful, I know.)_

Hooks do not only permit stateful behaviour, but they’re also _reactive_. You can use hooks to react to changes in the props of a component, store state, store references to mutable values, etc. And that’s just using the hooks shipped by default. Hooks are _composable_ too. This means I can write my own hook, that uses other hooks. By combining these, One can effectively inject state, behavior or reactivity into any functional component.

In this article I’m going to go over a few examples of these, both for React and React Native devs, that demonstrate this composability and create some neat and super handy, easily testable bits of functionality that you can use in any project.

## Universal Hooks

These hooks are usable for both React and React Native projects, as they are JS-only and use features available in both.

### useMounted

Quite often we may end up in situations where the consequences of a side-effect will unmount a component that’s expecting to make state updates. For example, let’s imagine the following example taken from an older codebase of mine:

`gist:tpjnorton/c27bb2d9844217e0072bf641b61cdd37`

Now, this component has a couple of issues that we won’t get into, but we should _really_ be handling the case where `props.onClick` rejects.

In any case, this is fine for our purposes. But what happens if `props.onClick` causes the app to navigate to a new page? This button will unmount, and, consequently, `setLoading(false)` gets called. In general, **you should never update the state of an unmounted component**, as this can cause memory leaks**.**

We need to know if the component is still mounted when we get to `setLoading(false)` , as we need to skip it if the component has unmounted in the meantime.

Let’s write a hook that gives us that ref:

`gist:tpjnorton/826a84525d86bb7c3c4e6187c62abdf5`

Now, we can use it in our previous example:

`gist:tpjnorton/62fb6627ef52f0379bdade883c64a08d`

This hook’s helped me avoid some subtle bugs. Moving swiftly on -

### useWebSocket

If you’re building an app that contains any kind of real-time communication (chat, games etc), it’s likely that you’ll end up using WebSockets. They give you full-duplex (two-way) communication with another host, contrary to HTTP which adopts a Request-Response model.

Handling the state of your WebSocket in a React Component can be a bit of a pain, and you’ve always got to remember to close your WebSocket on unmount, etc. In short, this leads to a lot of duplication if you make use of WebSockets in many places in your app.

Let’s take a look at the hook:

`gist:tpjnorton/e3f82e512f2a6600b07d1ba659f3ae63`

This hook returns a ref which stores the active WebSocket in its `current` property, and automatically closes it when the component using it unmounts, e.g.

`gist:tpjnorton/52865bf734b3a9ee83c5d552eb8c59c7`

This hook has lots of use cases for flexible WebSocket based applications, notably here chat, we can safely close the WebSocket when the chat is closed, opening it only when the chat is open.

Cool right? Given that we get a `ref` to an actual `ws` instance, it’s easy to add any extra behaviour we might want, using the usual WebSocket API.

### useLoading

This hook is a little time-saver. Imagine the scenario — you’ve got a component with `n` buttons, all of which launch some async operation on press/tap/click etc. Keeping track of their loading states is a bit of a pain — you don’t want to have to maintain an object or array of loading booleans, as it’ll be easy to cause bugs — especially if the number of buttons can change over the component’s lifecycle! It looks like this:

`gist:tpjnorton/ca4a66c4981691e8e7c0900a3248b8a9`

Now you have a view on the loading state of your action, encapsulated into a hook — no more `useState` cluttering your component, you’d do something like:

`gist:tpjnorton/21ab6d6f2d069f7dafe9f1cbe20c2d7c`

Because the wrapper makes sure to return the promise that your `async` function returns, you can do all the usual things you’d want to do with a promise ( `await` , promise chaining, etc.).

## React Native

Here’s a hook we can use in React Native applications to make our lives easier.

### useStatusBar

> EDIT: [This tutorial](https://reactnavigation.org/docs/status-bar/#tabs-and-drawer) from the `react-navigation` docs provides a more declarative version of this hook for achieving the same effect.

When writing a React Native app using React Navigation, we often run into a particular problem. In short, the device’s status bar is controlled by rendering a `StatusBar` component. But if we have an app with multiple screens, where more than one screen is rendered at once, rather than being lazy-loaded, this approach may fail. This is because rendering this `StatusBar` updates the config under the hood, but only reflects the state when it was last rendered. What this means is if your app has screens A and B, even if you have A visible, if B rendered after A, the status bar will have the config from screen B, which is wrong for screen A.

Luckily, the `StatusBar` module from React Native also exports imperative means of setting this config. So what if we could write a hook that makes these imperative calls when a screen focuses, so that we always have the right config? This is a hook I wrote for a project that does just so:

`gist:tpjnorton/1cc9b95e1216aca748539eda05866b7e`

Only drawback here is it’s not super future-proof for API changes.

Now we can set the config on each screen with just a call to `useStatusBar` and it’ll always be correct when the screen becomes focused. This hook-based alternative helps keep your markup clear of rendering `StatusBar` in every screen.

### Going Further — useAppearanceAwareStatusBar

What if we have an app that supports both dark and light modes using the phone’s color scheme + preferences, which could change behind the scenes? We can go one further:

`gist:tpjnorton/a15db455677b5cf6806527467911a8ff`
_A little more complicated, but super powerful!_

Now if the user changes their color preference in your app’s settings, or, for example, the sun sets and your phone’s color scheme changes, this `StatusBar` will be aware of it and match the rest of the app. Here obviously we’re not taking into account the other methods in the `StatusBar` API, which would be a welcome enhancement.

## Wrapping Up

As mentioned before, React apps can get same-y. We often find ourselves maintaining the same behavior for lots of components, and hooks are a great way to reuse behavior and make maintenance and iteration speed lightning fast.

Thanks for reading!

💙
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
        <item>
            <title><![CDATA[Animations in React Native Just Got a Whole Lot Easier]]></title>
            <link>https://www.tpjnorton.com/blog/posts/animations-in-react-native-just-got-easier</link>
            <guid>https://www.tpjnorton.com/blog/posts/animations-in-react-native-just-got-easier</guid>
            <pubDate>Mon, 05 Oct 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Ah, React Native animations. Long-considered to be one of the pain points of React Native development, due to their steep learning curve…]]></description>
            <content:encoded><![CDATA[
![cover image](https://miro.medium.com/max/800/0*gId0r_2j1sWTLCEJ)

Photo by [Oskar Yildiz](https://unsplash.com/@oskaryil?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

Ah, React Native animations. Long-considered to be one of the pain points of React Native development, due to their steep learning curve and being packed with potentially performance-crushing pitfalls, creating delightful animations and gesture-based interactions has been hard. But why so tricky? The answer lies in React Native’s architecture, namely with something called the bridge. In order to understand why, let’s have a brief look at React Native’s architecture to find out more.

## Architecture

In order to provide close-to-native performance, React Native uses a dual-thread architecture – a JS thread, which runs your React Native code, and a native UI thread. Your React Native components are effectively specifications for native UI components – layout is taken care of by a Flexbox engine – you in theory get Native UI performance (in reality, your mileage may vary) but get to use your favourite UI framework, React, with some caveats. These two threads communicate via a glorified pipe with some extra features known as the bridge. This bridge is fully asynchronous, and lets one call to native code from JS, also asynchronously. The official React Native docs explain in more detail.

The bridge is often the performance bottleneck in React Native projects, as communication is orders of magnitude slower than most other operations (especially on Android). This isn’t because it’s poorly written, it’s just the nature of the beast. In any case, minimising over-the-bridge communication is a good general strategy for achieving native-like performance in any app.

## Animations and the Bridge

Looking at this architecture, animations stick out as something very difficult to do performantly. How are we supposed to achieve native performance if we need to, for example, send an object’s new position over the bridge once a frame, 60 times a second? Thankfully, the engineers working on React Native thought of that. What if we could use the native animation engine on both platforms? That way the animation gets done natively, no bridge comms needed. So that’s what we can do in React Native – specify an animation in JS that gets executed on the UI thread. This approach is possible with the Animated API that ships with React Native, but again we’re hit with some limitations. Some style properties can’t be animated using the UI thread, so we’ve got to fall back to the JS thread for those. And, as expected, performance is not great (specifically on lower-end Android devices), and I’m being diplomatic.

## React Native Reanimated

In a bid to solve this issue, a group of engineers in the community decided the Animated API didn’t offer real low-level access to animations, and no granular way to express animations and no easy way to create gesture-based animations, so enter `react-native-reanimated`. Reanimated solves all these problems, providing the promised low-level abstraction over native animations.

But something still wasn’t right. Reanimated v1 suffers from a very steep learning curve, as complex native animations using the library have to be expressed with a pure functional node-based style, which is not in the wheelhouse of most developers. For instance, here is an excerpt of an animation to make a circle grow and shrink repeatedly:

`gist:tpjnorton/4c8514fd2eee96e4dbdd554c4f9a3ccc`

Not the easiest thing to get one’s head around.

Still, the community came to the aid of struggling developers, with excellent video series showing the power of Reanimated by awesome people such as William Candillon demystifying things for everyone, and recreating complex interactions in popular native apps in React Native with Reanimated. Even with this, the conclusion for me and many other React Native developers was this: you could do all the delightful stuff you see in native apps, but it was too complicated to learn. Any solution developed by the community, no matter how accomplished, kept running up against the limits React Native’s architecture, thus became more and more complex and more difficult to learn. Thankfully help was just around the corner.

## TurboModules

For the last while, the Facebook team have been hard at work on a re-architecture of React Native, to solve some of the problems we’ve seen so far. It’s complicated, and if you’re looking for a more in-depth look at the re-architecture this series is excellent.

Anyway, the short version is: You don’t need to cross the bridge to talk to the native side anymore. What this means for us is that you can now share animation (or indeed any) values between native code and the React Native thread, without having to cross the bridge. Hooray! These new native modules that can be interacted with without crossing the bridge are called TurboModules.

## Reanimated 2

Version 2 of React Native Reanimated is on the way, currently in alpha — and it's amazing. It’s completely different to v1. The latest alpha is even supported by the managed workflow in Expo SDK 39 🔥

By using this new architecture, all of the headaches caused by the bridge are removed; we no longer need to specify our animation purely functionally as nothing needs to get serialized and sent over the bridge before the animation begins to enjoy native level performance.

We can now see and interact with the animation values on the UI thread, meaning we can now inspect as well as drive the animation imperatively from the JS thread!

### Worklets

Reanimated 2 introduces the concept of a _worklet_. This is a JS function that can run in a tiny JS context on the UI thread. It’s dead easy to turn a regular JS function into a worklet using a directive:

`gist:tpjnorton/01afcb6b0a4b46b37a662186b7d22842`

That `'worklet';` directive basically just means that this function can be executed on the UI thread.

### Shared Values

Because of the lack of a bridge, animated values are now referred to as shared values, because they are quite literally shared with the UI thread.

This allows us to modify them directly and update an animation’s progress from the JS thread simply by doing the following:

`gist:tpjnorton/5abb2ac67e6a3b90b59a484b5001cae9`

Here `useSharedValue` is a convenient React Hook shipped with Reanimated 2 to create a shared value. `useAnimatedStyle` is another helper from Reanimated which ensures our component re-renders when the animated value updates.
This component moves 100 pixels to the right after one second after mounting.

Piece of cake 🍰

If we wanted this transition to animate smoothly, the team behind Reanimated thought of that.
We can wrap our assignment in an animation helper to animate the transition:

`gist:tpjnorton/c42fc7b634214939f9ff2a39fd3ca39f`

Now the value will smoothly transition to the target value `100` over the course of 1000ms. Neat!
Combining what we learned, let’s see how the circle animation before looks with the new API:

`gist:tpjnorton/bf1846ac7f2929b9222f911f0659bfbd`

How easy was that?

That’s not all — it’s now just as easy to respond to gestures and other input events, then trigger & update animations in the JS thread imperatively and get native animation performance, as shown in this video. As before, you can do the delightful stuff you see in native apps. But this time it’s easy.

## In Closing

It’s extremely exciting when something like this happens. React Native is now more than 5 years old and it’s so great to see improvements as fundamental and value-adding as this after as much time. Amazing work and support is still being devoted to the project by an accomplished and helpful community. Thanks guys!

tl;dr — See title.

Thanks for reading!

💙
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
        <item>
            <title><![CDATA[Better React Code Using Functional Programming]]></title>
            <link>https://www.tpjnorton.com/blog/posts/better-react-code-using-functional-programming</link>
            <guid>https://www.tpjnorton.com/blog/posts/better-react-code-using-functional-programming</guid>
            <pubDate>Wed, 30 Sep 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[We’ve all come across stuff like this before. You’ve got a component that can display errors locally, but calls external code...]]></description>
            <content:encoded><![CDATA[
![cover image](https://miro.medium.com/max/800/0*f8XHM9t176gJpQ5D)

Photo by [Max Chen](https://unsplash.com/@maxchen2k?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

We’ve all come across stuff like this before. You’ve got a component that can display errors locally, but calls external code:

```jsx
import { someApiCall, someOtherApiCall } from './api';

const SubmitButtonGroup = () => {
  const toast = useToast();
  return (
  <>
    <Button onClick={someApiCall}>
      Call
    </Button>
    <Button onClick={someOtherApiCall}>
      Go
    </Button>
  <>
  )
}
```

Note: I’m being very generic here with my component names, and an imaginary toast hook based off the one from Chakra UI.

Looks fine, right?

But what if either of those calls fail?

You tell yourself you need to write an error handler. So you go ahead and write the following:

```jsx
const onApiSubmit = async () => {
  try {
    await apiCall();
  } catch (e) {
    toast('Oh no!', e.message, 'error');
  }
};
```

Then you add that to the local scope of your component. Not too bad. You’ve now handled that error and sorted it for the user.
Then you realize that you’re going to have to do the same thing for that other API call. So you chug along and write the other wrapper:

```jsx
const onOtherApiSubmit = async () => {
  try {
    await someOtherApiCall();
  } catch (e) {
    toast('Oh no!', e.message, 'error');
  }
};
```

You’ve just basically written the same thing twice. Now picture the scene for three, or five buttons.

It’s going to be a long day.

Fortunately, we can do better, using a little concept from functional programming - known as a Higher-Order Function.

## Higher Order Functions

According to Wikipedia, a Higher-Order Function is: “a function that does at least one of the following:
takes one or more functions as arguments (i.e. procedural parameters),
returns a function as its result”.

For example, if you’ve ever used `map`, `filter` or `reduce` in JavaScript, you’ve used a Higher-Order Function. This is because they are **functions you call, giving them a function to call on each element**. The most classic example in the Web world is probably JavaScript’s `setTimeout` :

```js
const callback = () => alert('something happened!');
setTimeout(callback, 1000);
```

`setTimeout` is a Higher-Order Function because it takes a callback as a parameter, a function to be executed later. This is a very common pattern we saw in the early days of JS.

## Putting Higher-Order Functions To Work

So returning to our examples above:

```jsx
const onApiSubmit = async () => {
  try {
    await apiCall();
  } catch (e) {
    toast('Oh no!', e.message, 'error');
  }
};
const onOtherApiSubmit = async () => {
  try {
    await someOtherApiCall();
  } catch (e) {
    toast('Oh no!', e.message, 'error');
  }
};
```

Can you spot the WET code here?

The only difference between both these wrappers is the actual API call that gets made. Every other part of those functions is the same.
So what if we could make the wrapper into a Higher-Order Function? That way, the API function could be given as an argument to a single wrapper.

```jsx
const withErrorHandling = (callback) => async () => {
  try {
    await callback();
  } catch (e) {
    toast('Oh no!', e.message, 'error');
  }
};
```

There we go! A single wrapper for that can be used for as many actions as you want. So how do we use it? Simple! Let’s update our example from before:

```jsx
import { someApiCall, someOtherApiCall } from './api';

const SubmitButtonGroup = () => {
  const toast = useToast();
  const withErrorHandling = (callback) => async () => {
    try {
      await callback();
    } catch(e) {
      toast('Oh no!', e.message, 'error');
    }
  }
  return (
    <>
      <Button onClick={withErrorHandling(someApiCall)}>
        Call
      </Button>
      <Button onClick={withErrorHandling(someOtherApiCall)}>
       Go
      </Button>
    <>
  )
}
```

**Much** better. One handler, that provides local error handling to external code. A solution that scales! 🙌

This is just a simple example to show how we can use functional programming on the Web to create concise, elegant applications. There are many more other things we can do in the same vein to make improvements, which I’ll cover in other posts.

I use functional programming daily as a React & React Native developer. In fact, I can’t remember the last time I actually wrote a `for` loop. It is, in my opinion, one of many luxuries we web developers enjoy.

Thanks for reading!

💙
]]></content:encoded>
            <author>info@tpjnorton.com (Tom Norton)</author>
        </item>
    </channel>
</rss>