Recent Posts | Archive


Be I/O

The AQI is X. Should you be outside?

Climate change is here

I’ve been living in Chicago for most of my life, and this was never really a thing until a couple of years ago. Since then, it’s happened every year, from fires in California, Colorado, Canada, or elsewhere. The smoke comes, sticks around for a couple of weeks, and gets better or worse based on the direction of the wind.

In late July, Chicago rapidly became enveloped in a cloud of smoke that was worse than anything I had ever experienced: the AQI was over 700 for nearly a day.

I’ve been running close to 60 miles per week, so when I woke up one morning to find my own bedroom smelling like smoke, I panicked and needed to find someplace to run. I hate running on treadmills and dislike going to gyms, but at least the decision to not run outside was easy. In the days that followed, though, as the air quality changed dramatically with the wind, I found myself spending 10–15 minutes every morning looking at as many as five apps, trying to decide whether it was yet another day when I needed to run while staring at a wall.

Why are all these apps telling me completely different things? I wondered.

So I decided to get my own sensor and trust it rather than third-party sources.

PurpleAir Zen sensors were on backorder, so I had two weeks to think about what I wanted to do once I had access to the raw data that could tell me whether to run inside or outside.

Once I got the sensor, I had a pretty solid idea for a crazy-simple app: it would take the numbers from a recent metabolic assessment I’d done—which documented the volume of air I took in alongside my heart rate—and reverse-engineer the WHO’s guidance on PM2.5 intake to decide whether a workout would push me above the limit or keep me below it.

What I didn’t know was a ton of other crazy stuff:

  • Government-managed sensors are super well calibrated but post data two to three hours late.
  • Personal sensors—like the Zen I had just bought—mistake humidity for particulates, so they require dramatic correction algorithms, sometimes halving the reported concentration.
  • PurpleAir’s API is expensive AF.
  • AirGradient has two APIs, and the sensors are identified using different values of locationId in each, with no one-to-one mapping.
  • Vibe-coding apps is intoxicating but crumbles quickly.
  • Vibe-coding apps is drowning App Store reviewers in submissions, changing the development lifecycle of an app completely.
  • Putting the last two things together resulted in this being the first side project I’ve worked on that was stressful and, frankly, not that fun—even if the result is awesome and I use it every day.

The result of all of this is Be I/O, an app I mostly vibe-coded (my first and last vibe-coded project, at least for a while).

Problems solved and problems not

It solves exactly the problem I had: when I can’t decide whether the air is good enough to run outside because it’s right on the cusp of WHO guidance, the app has enough data about me and my plan that I can trust its decision rather than waffling back and forth.

It also introduced some problems. I typically know my apps inside and out, so when I see some strange behavior, I know almost immediately what it is. And if the strange behavior is bad, I can respond quickly and ship a fix. But by vibe-coding this, I had no choice but to ask Claude what went wrong. And even if I had a fix, the ridiculously slow App Store review process—three weeks!—meant that I had little choice but to accept that there was a bug in production and that there would be one for a while.

None of my side projects make actual money, so I work on them for a combination of reasons: to solve problems that I have and because I derive immense joy from actually building things. I work mostly on iOS both because I love interacting with apps I wrote with my own fingers and because I hate managing servers. So while Be I/O was a success on those levels, I plan on scaling back my Claude use and refactoring the app until I understand it.

Fun things

There were some really fun things that came out of this, though.

API keys

I don’t ever want to manage a server again, so building apps that use authenticated APIs can be a challenge. You can’t bundle a key in the app because it’s so easy to find and use. You can’t store a key on a server and willy-nilly dish it out for the same reason. But during those two weeks when my Zen was on backorder, I had an idea so brilliant that I’ll use it again many times:

Be I/O uses Cloudflare Workers for its API. In only a few hundred lines of TypeScript, the Worker proxies requests from the app to three different authenticated APIs, injecting the required keys along the way and aggressively caching to minimize calls.

That’s not a novel idea: Cloudflare Workers have gotten super powerful, take close to zero time for cold starts, and are so cheap that, at my volume, I won’t need to worry about paying for them for a while.

But the brilliant part is how I make sure my API only serves my app: require a subscription.

Be I/O has an ultra-cheap subscription because I can use the receipt for authentication. Apple has a framework called App Attest that does something similar, but it requires storage, which is too complex for my don’t-want-to-manage-servers side projects. My API can take apart a receipt, verify that the root certificate is Apple’s, and check that it includes my bundle ID—all in 29 milliseconds. That’s not free, but it’s pretty damn close.

Cloud stuff for iOS is 100x better than it used to be

You can use GitHub Actions for CI/CD for pretty cheap! You can use Xcode Cloud for even cheaper! Holy macaroni, I’ve been having so much fun getting all my apps hooked up with Xcode Cloud for CI/CD and using GitHub Issues and Milestones to manage my planning. I started doing this because of my distrust of Claude, but ended up absolutely loving it because, Claude or not, I’m confident now about what I’m shipping and spending less time wondering what I should do next.

I love building things with my own hands

The stress I experienced when I discovered that Claude had built this app on a fundamental flaw—yadda yadda yadda, yes, I know I’m responsible for this, but a stubbed value looks like a reasonable thing—that I couldn’t fix because Apple was taking so long made me want to work on my other projects.

So while I was waiting two-plus weeks for Apple to review Be I/O, I shipped updates to four of my five published iOS apps, half a dozen or more of my web projects, and got CI/CD running in the cloud for almost all of them. Claude helped me with test coverage, for sure, and helped me validate ideas before I spent too much time banging my head against the wall. But mostly, Claude reminded me how much I love building things with my own hands, “10x productivity” or not.


Anyway, it’s been a journey, and I’m very happy with the result. And the app is good too.

#permalink

Wrigley Calendar

Last week, I got home from an exhausting trip to San Francisco, and collapsed into my wonderful bed, so relieved to be home.

And then at 10:30pm, I was awoken to the sound of firecrackers and really loud music. It was so loud that I assumed someone was in our alley, right outside our bedroom, being obnoxious.

But no! It was Mumford & Sons, being totally inconsiderate, ignoring the limits of their permit and playing well past the allowed 11pm (which is already late!).

This is not the first time this has happened, and anyone who says things like “Wrigley has been here for years, so you knew what you were getting into” doesn’t realize just how absurd the noise level is for these concerts. We’ve lived here, about half a mile from the venue, for almost ten years, and in the neighborhood even longer. The volume just gets worse and worse. So to the haters out there, I say: If you want a loud concert, maybe an outdoor venue isn’t the right one?

On top of that, I hate the Cubs, and hate what so many of the blue people do to this neighborhood. I get that you’re looking to have a good time; I have no problem with that. But maybe pee in the bathroom instead of my alley? Or stop drinking before you need to puke on our sidewalks? Or take the red line, bus 22, bus 77, bus 36, or bus 76, rather than driving in from the suburbs and impatiently speeding through the stop sign my kids are standing near?

So… I asked Claude to make a calendar feed that combines the Cubs feed (excluding away games) and Ticketmaster (for concerts). I don’t want to manage a server, so it’s using Github actions for updates.

Subscribe here:

https://raw.githubusercontent.com/thelowlypeon/wrigley-calendar/main/wrigley.ics

See or contribute to the project on Github.

#permalink

Kythe

A little over a year ago, my sister Nell had an idea for an app and set out to build it.

The idea is simple: there are a lot of people in our lives that mean a lot to us, but too often, if there’s not a specific reason to reach out, our relationships get put on the backburner and the barrier to bring them closer gets higher and higher. So what if we had a way to just give us a little nudge to… break the ice?

I was an early technical advisor, helping with some of the early and foundational decisions, and, ultimately, helping to find a technical partner to work with long term. Despite the shakey start, I am so happy about how it turned out, and super proud of Nell and her team for what they’ve accomplished.


Kythe is about fostering a deep sense of real, human connection with those you love.

You and your loved ones create a circle, and every few days, GPT asks a question based on your common interests. For example, I love chatting with my inlaws, but our lives are so different there often isn’t anything to talk about, so we end up going months without chatting. I’ve been using it for a few months now and love it. And the iconography is delightful.

I highly recommend checking it out!

#permalink

2025: A year in running

I’m not usually one for end-of-year retrospectives, but 2025 was a big year for running, so it feels appropriate. I fit in a lot of miles, in a lot of places, for the first time had a coach, and under his guidance trained using this crazy new approach called the Norwegian Singles (NSA).

2025 Goals

I started the year, as I tend to do, with a review of my high-level goals. These three goals haven’t changed much since I started running Peterthons:

  1. Injury prevention
  2. Run as many miles, over as many hours, in as many places, as possible, without breaking goal #1
  3. Get faster (or more efficient), keeping in mind goals #1 and #2

Again as usual, none of these goals are very specific, but I had a few of those as well:

  1. Run a fast Peterthon
  2. Run a trail ultra at a consistent pace

One of my best friends got married the day of the 50k I was planning on running, so I postponed it. And then, for the third year in a row, the poor air quality caused by North American forest fires took a few critical weeks out of my training season, forcing me to adjust my plans. So instead of an ultra, I ran a second, “fun”, Peterthon.


Injury

I’m extremely grateful to have had no major injury. But I wasn’t fully injury free: a purple big toenail took me out for a very frustrating week. I wore a new pair of shoes that both made my feet sweat a lot, and allowed them to move too much. The unfortunate result was that once I landed in Paris, my nail was purple, and it was only a matter of time before I could barely walk. I’ll take this over a broken shoulder any day, but it still sucked: it made it harder to be on vacation, and meant I couldn’t explore new cities by my preferred mode (running). Furthermore, my foot did weird things to compensate, so by now, six months later, other toes on that foot are still recovering from pull the extra weight.


Miles

By the numbers

As I write this, I’m on course to run just over 1,800 miles, making it my second biggest year, behind 2021 (1,994 miles). It’s not quite an apples-to-apples comparison, because other than 2019, when I rode 1,242 miles, I always ride a lot too. (This year I’ve ridden just under 1,700 miles, compared to 4,000 in 2020, or 3,000 in 2023.)

By time – which is again sketchy because sometimes I log commutes and sometimes I don’t – I’ll have logged around 600 hours, the third most, behind 2024 (634 hours), and 2023 (607 hours).

But the reason I consider 2025 a big year for running is all the other stuff that happened.

By location

  • I ran the entirety of the Champs-Élysées. Not as cool as riding it, but on the way back to the hotel…
  • I ran past Eliud Kipchoge on the Seine, and can’t remember a time I was more star-struck.
  • I got 59 explorer tiles in Europe (in France, Germany, and the Netherlands)
  • I got a handful more tiles in Missouri, New York, Wisconsin, and Texas

Performance

The Norwegian Singles Approach is no joke. While it didn’t help my marathon time much, I crushed my previous bests at shorter distances (all unofficial times for pre-measured distances, unless otherwise noted, because that’s how I roll now):

  • Mar 09: 19:29 5k (unofficial PR)
  • Apr 06: 89:42 Half Marathon (unofficial PR)
  • May 24: 3:19:28 Marathon (Peterthon V)
  • Jul 18: 19:35 5k
  • Jul 25: 5:36 1mi (unofficial PR)
  • Sep 21: 19:53 5k (official PR)
  • Oct 24: 5:43 1mi
  • Nov 23: 3:21:48 Marathon (Peterthon VI)


Peterthon V

I was feeling good for Peterthon V. NSA had given me a strong boost of confidence, and now that Peterthons are more routine, I slept well, rather than staying up all night being needlessly anxious.

Unfortunately, I overestimated my abilities. I ran North to Plaza del Lago, and my parents came out to cheer me on at the Bahai Temple. The route there is very familiar to me, but what I didn’t realize was how different it is to just get there than it is to follow a specific, measured route; the mental energy required to follow my pre-defined course was immense. I struggled.

I still ended up with a decent time, but definitely feel like I could’ve done better.


Peterthon VI

This was my “fun” Peterthon of the year, and I’m grateful it wasn’t my fast one: I lost a couple weeks due to the air quality, and for the days leading up to it felt a cold coming on. Unlike Peterthon V, though, I kept the route simple, and very familiar. (I had to make a small adjustment out of fear that the Oak Street curve would be under water.)

I put in much less effort than Peterthon V, thanks to Brittney, who ran her relaxed Brittneython the same day. Not only was that great encouragement and very motivating, but her attitude about it made me actually treat this like a fun marathon.

And in the end I only did 2 minutes worse than Peterthon V. ¯\_(ツ)_/¯



Other stuff

Atreyu

Trump has proudly destroyed a lot of good things, but this one crushed me – the tariffs killed my all-time absolute favoritest ever shoe company, Atreyu. I bought as much of their inventory as I could, but unfortunately they only had two pairs of my favorite (Base Model v3) so it’ll be sad when those are worn out. (I also got four pairs of their Daily Trainer v2.)

I love these shoes. Even though this section is a small percent of this rambling post, Atreyu shutting down is more than half of why I wanted to post this at all.

Motivation

Work and life have been stressful lately, and together with the cool balance of NSA, my relationship to running has changed. I find myself less motivated to try really hard, and more motivated to make this sustainable enough to last me well into my 50s or beyond. It’s become so important to me, though, it’s worth moving other aspects of my life around to make room for it.

#permalink

Press Here!

Over fall break last month, my daughter Lily had an idea for an app.

I told her if she wanted to make an app, she had to think of the simplest idea she could, cut out half the features, then further simplify what remained. And she knocked it out of the park:

A pop-it/fidget app, where you can change the color and size of the popits, with haptic feedback.

It took me a couple hours to write it up, and some back and forth with the two designers in the house (my girls), and, voila! Introducing…

Press Here!

#permalink

View Archive