<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://runcode.blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://runcode.blog/" rel="alternate" type="text/html" /><updated>2026-06-11T15:39:17+00:00</updated><id>https://runcode.blog/feed.xml</id><title type="html">run; code; blog;</title><subtitle>my daily routine. a blog by joseph roque.</subtitle><author><name>Joseph Roque</name></author><entry><title type="html">Achieving Greatness at the Fraser Street Beer Mile</title><link href="https://runcode.blog/2026/06/11/fsrc-beer-mile-2026.html" rel="alternate" type="text/html" title="Achieving Greatness at the Fraser Street Beer Mile" /><published>2026-06-11T15:37:00+00:00</published><updated>2026-06-11T15:37:00+00:00</updated><id>https://runcode.blog/2026/06/11/fsrc-beer-mile-2026</id><content type="html" xml:base="https://runcode.blog/2026/06/11/fsrc-beer-mile-2026.html"><![CDATA[<p>On May 30th I lined up for the 3rd annual Fraser Street Beer Mile. It would be my 5th ever beer mile, and ended with my first podium finish in a race, ever.</p>

<h2 id="what-is-a-beer-mile">What Is a Beer Mile?</h2>

<p>A beer mile is a footrace, typically run around a track, with one gimmick: competitors must drink a beer before each lap of the track, completing 4 beers and 4 laps by the end of the race.</p>

<p>The race goes like this: competitors line up, beer in hand. The beer is a choice left to the competitor, but it must be at least 5.0% alcohol, and it must be at least 355ml (can or bottle). At the gun, competitors open their first beers and start moving. They must finish their beer before exiting a short “transition area”. Upon finishing, they can start their first lap. Once the lap is finished, they grab a second beer, and the process repeats for 4 laps in total.</p>

<p>Running and drinking is hard, but the real challenge is in keeping the beers down. Competitors must drink their entire beer, and if you throw up at any point during the race, the penalty is running an extra lap. In trying to drink the beers as quickly as possible, the beer tends to foam, and you’ve really got to focus on not letting it bubble its way back up your throat.</p>

<p>The current world record? As of writing, it’s held by Corey Bellemore, who ran a blistering 4:27 beer mile. If you want to know more, there’s a set of official rules and more results at <a href="https://www.beermile.com/about/rules/1">beermile.com</a>.</p>

<h2 id="my-history-as-a-beer-miler">My History as a Beer Miler</h2>

<h3 id="2019-running-my-first-ocrc-beer-mile">2019, Running My First OCRC Beer Mile</h3>

<p><img src="/assets/posts/fsrc-beer-mile-2026-2019-1.jpg" alt="Grayscale photo of me during the OCRC 2019 Beer Mile, drinking a beer." /></p>

<figcaption>Grayscale photo of me during the OCRC 2019 Beer Mile, drinking a beer.</figcaption>

<p>I ran my first beer mile in 2019, hosted by the Ottawa City Run Club, at a tiny residential track in the suburbs of Ottawa. The run club would host interesting little races like this from time to time, and it was the first time I had heard of the beer mile. I signed up, just hoping to have fun with it, have a few beers with friends, and try something new.</p>

<p>The race ended up going…. alright. My primary goal was to just not throw up, and I did at least accomplish that! But it was certainly close. The way the foam creeps up your throat is hard to describe, but it really does feel like it’s going to force its way out.</p>

<p>I ended up finishing in 11:51, my beer drinking and laps getting slower and slower over the course of the race. The race wasn’t officially timed in any way, but I’ve got my <a href="https://www.strava.com/activities/2452972523">results on Strava</a>.</p>

<p><img src="/assets/posts/fsrc-beer-mile-2026-2019-2.jpg" alt="Me during the OCRC 2019 Beer Mile, making a face of disgust while holding a beer in my right hand" /></p>

<figcaption>Me during the OCRC 2019 Beer Mile, making a face of disgust while holding a beer in my right hand</figcaption>

<h3 id="2024-the-renegade-mile-and-an-invite-to-the-vancouver-beer-mile">2024, The Renegade Mile, and an Invite to the Vancouver Beer Mile</h3>

<p>In 2024, I discovered my favourite Vancouver run club, the East Van Run Crew, hosted their own beer mile. Unfortunately, I discovered this, along with some friends, just slightly too late — the race had already sold out. But, undeterred, we decided to run our own beer mile an hour before they had scheduled to run theirs, at a nearby track.</p>

<p>Turns out, we had actually ended up at the same track the Vancouver Beer Mile was planning to run at, and they offered to time our race for us. We lined up, and I was running my 2nd ever beer mile.</p>

<p>The race went well, I PR’d by quite a bit, finishing in 7:45, keeping the beers down, and pushing through the runs quite a bit faster. In this attempt, somehow almost every lap was faster than my fastest lap in 2019. From a fastest lap time of 2:02 in 2019, to a slowest lap time of 2:15 according to <a href="https://www.strava.com/activities/11381699052">Strava</a>.</p>

<p>So impressed by our ambition, Cory, race director of the Vancouver Beer Mile, invited us to compete in the subsequent official race, just 45 minutes later. Not one to back down from a challenge, I decided I’d go for a second round, and my 3rd ever beer mile.</p>

<p><img src="/assets/posts/fsrc-beer-mile-2026-2024.jpg" alt="Me during the 2024 Vancouver Beer Mile, drinking a beer" /></p>

<figcaption>Me during the 2024 Vancouver Beer Mile, drinking a beer</figcaption>

<p>You’d think 2 beer miles in a row would be hard, but there really wasn’t much to the 2nd one. The first 4 beers had nearly an hour to settle, and I had definitely begun to feel them. It almost made the running easier, to be a little tipsy… almost.</p>

<p>In the end, I followed up my 7:45 beer mile with an 8:10 beer mile. I do recall being absolutely goofy that evening, having 8 beers in just over an hour.</p>

<h3 id="2025-the-vancouver-beer-mile-and-becoming-delusional-about-the-podium">2025, The Vancouver Beer Mile, and Becoming Delusional About the Podium</h3>

<p>In 2025, I came back to the Vancouver Beer Mile, having successfully registered for a spot in advance, and I was really starting to have some confidence in my beer-mile-running ability. I had only ever really paid attention to my friends, but I knew at that point I had become the fastest beer miler among them. In the lead up to that race I really started thinking a bit more about not just doing well against my previous attempts, but doing well in the race overall.</p>

<p>I had settled on a couple beers that sat well in my stomach and felt I could confidently keep down, I was generally a fast runner, faster than I had been at almost any other point in my life. I felt locked in.</p>

<p>The race itself is a blur. I remember friends cheering me on, I remember the feeling of the foam travelling up my throat, I remember trying to catch my breath before starting to chug the next beer. While this one’s <a href="https://www.strava.com/activities/14431360791">on Strava</a>, it’s also <a href="https://sportstats.one/event/vancouver-beer-mile/leaderboard/143618">on Sportstats</a>. Those told a clear picture: I was a fast runner, but a slow drinker. On average, I was running ~1:16 laps, and spending ~33 seconds per beer. The winner? ~1:12 laps, but just ~17 seconds. That was a huge gap. However, the podium wasn’t so far off. 3rd place was running ~1:21 laps (slower than me), but finishing his beers in ~15 seconds (much, much faster than me). I figured maybe I had a bit more in my legs, but I <em>knew</em> I had a lot more in my drinks.</p>

<p><img src="/assets/posts/fsrc-beer-mile-2026-2025.jpg" alt="Me, dressed as Spider-Man, during the 2025 Vancouver Beer Mile, holding a beer and wearing bib 777" /></p>

<figcaption>Me, dressed as Spider-Man, during the 2025 Vancouver Beer Mile, holding a beer and wearing bib 777</figcaption>

<h2 id="2026-putting-it-all-together-at-the-fraser-street-beer-mile">2026, Putting It All Together at the Fraser Street Beer Mile</h2>

<p>In 2026, the Vancouver Beer Mile organizers took a step back, and the Fraser Street Beer Mile took over. Costumes highly encouraged, I donned my Spider-Man suit once more. This year I was hoping to take up to a minute off my time, knowing I could drink the beers much faster than I had been in the past, and hoping to sneak my way onto my first podium.</p>

<p>At this point in my illustrious career as a beer miler I was confident I could keep the beers down while running. Once I finished a drink, got moving, and got the first couple burps out, the rest was easy. Stay cool, stay fast, don’t lose the leaders.</p>

<p>I started out in 6th place, finishing my first beer in 14 seconds, and heading out on the first lap around the track. Starting out in 6th had me slightly worried, but I felt confident I could get the next 3 beers down with some consistency, that I hoped the other racers wouldn’t be able to muster. I finished the first lap in 1:19, not my fastest, but I knew holding back just a touch was going to help with the beers.</p>

<p>For the 2nd beer, I did my best to get it down in one breath. In my opinion, that is what makes or breaks a strong beer mile. You finish a lap, it’s just shy of an all-out sprint, you’re gasping for air, and now you’ve got to hold your breath while you put back a beer so it doesn’t go down the wrong pipe. If you stop now, you’ll have to catch your breath again, and it’s only going to take longer. Hold your breath, suck the beer back, and get back to running as quickly as possible.</p>

<p>The 2nd beer ended up taking me 22 seconds, and I headed out, 8 seconds under my time at this point from 2025. I left the transition zone in 2nd place by just a second or two, but over the course of the next 200m I closed that gap. I lost a few seconds on the lap, leaving me just 3 seconds under my time from 2025, but in first place overall!</p>

<p>The 3rd beer went down surprisingly smoothly, but just a bit more slowly. I did end up having to pause and catch my breath a second time, and finished this beer in 26 seconds. However, that put me well under the 40 seconds it had taken me in 2025, and I headed out on the 3rd lap, still firmly in first place.</p>

<p>On the 4th lap, I, once again, stopped to catch my breath, but still put the beer back in just 27 seconds, giving me, to this point, a ~35 second lead over my 2025 result. I headed out on the last lap in 1st place, unbeknownst to me that 2nd place would kick that last lap, leaving on their 4th lap 10 seconds behind me, and finishing just 1 second behind me.</p>

<p>In the end, I’d finish the Fraser Street Beer Mile in 6:45, a 35 second PR over my 2025 beer mile, and in first place overall! For the first time in my life, I had not only finished on the podium, but had actually won the race! I was a bit in shock, that I had held myself together and legged out a win.</p>

<p>The full results of the race are <a href="https://sportstats.one/event/fsrc-beer-mile/leaderboard/146631">on Sportstats</a> and my own Strava activity is <a href="https://www.strava.com/activities/18721741757">here</a>.</p>

<p><img src="/assets/posts/fsrc-beer-mile-2026-2026-podium.jpg" alt="3 people in costumes in a row. Myself, on the left, dressed as Spider-Man, holding a disco ball. 2 other men to my right, dressed as a thumbs-up and a cigarette box, 2nd and 3rd place, respectively." /></p>

<figcaption>3 people in costumes in a row. Myself, on the left, dressed as Spider-Man, holding a disco ball. 2 other men to my right, dressed as a thumbs-up and a cigarette box, 2nd and 3rd place, respectively.</figcaption>

<h2 id="whats-up-next">What’s up Next?</h2>

<p>The 2026 edition of the Fraser Street Beer Mile won’t be my last. I know there’s still so much room for improvement. There’s a huge gap between my average ~22 seconds per beer, and the elite beer drinking times of ~6–8 seconds. My laps are quick, but they could be quicker. The past couple years my focus has been on longer (<a href="https://www.strava.com/activities/15485063503">much longer</a>) races, but I’ve been thinking about stepping back from focusing on ultramarathons as my A-races each year, and diving back into the world of road racing. My eventual goal is to run a sub-6:00 beer mile. With a focus on speed and drinking, I think there’s plenty of space to find those ~45 seconds of improvement.</p>

<p>For now though, I’ve got my <a href="https://raceroster.com/events/2026/116093/seek-the-peak-2026">next</a> <a href="https://trailrunning.ca/race/alpenglow/">couple</a> races to focus on, and my wedding in the very near future.</p>

<p>But I’ll be back to take on the Beer Mile once more, in due time.</p>]]></content><author><name>Joseph Roque</name></author><category term="running" /><category term="personal" /><category term="race-report" /><summary type="html"><![CDATA[On May 30th I lined up for the 3rd annual Fraser Street Beer Mile. It would be my 5th ever beer mile, and ended with my first podium finish in a race, ever.]]></summary></entry><entry><title type="html">early birds</title><link href="https://runcode.blog/2026/04/21/early-birds.html" rel="alternate" type="text/html" title="early birds" /><published>2026-04-21T20:35:00+00:00</published><updated>2026-04-21T20:35:00+00:00</updated><id>https://runcode.blog/2026/04/21/early-birds</id><content type="html" xml:base="https://runcode.blog/2026/04/21/early-birds.html"><![CDATA[<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>We spent the morning
walking alongside early risers and early birds
chasing worms and water droplets dripping
off the backs of ducks.

My brother asked about pierogi and potatoes
and we didn't have an answer
but you knew someone who did
and before I knew it
I was a telephone operator
relaying messages between two parties
closer in distance to each other
than I was to either of them
but practically a world apart
if not for me.

We didn't spot a single new bird
only old birds
borrowed birds
blue birds.

We'll try again next month
but there's few new birds left for us to find
we'll settle for old birds
and each other's company.
</code></pre></div></div>]]></content><author><name>Joseph Roque</name></author><category term="poetry" /><category term="writing" /><summary type="html"><![CDATA[```plaintext We spent the morning walking alongside early risers and early birds chasing worms and water droplets dripping off the backs of ducks.]]></summary></entry><entry><title type="html">Ins &amp;amp; Outs for 2026</title><link href="https://runcode.blog/2026/01/13/ins-outs-2026.html" rel="alternate" type="text/html" title="Ins &amp;amp; Outs for 2026" /><published>2026-01-13T03:46:00+00:00</published><updated>2026-01-13T03:46:00+00:00</updated><id>https://runcode.blog/2026/01/13/ins-outs-2026</id><content type="html" xml:base="https://runcode.blog/2026/01/13/ins-outs-2026.html"><![CDATA[<p>Late last year I was invited by a friend to his ‘Ins &amp; Outs’ party for 2026. The basic premise was that everybody would bring a list or powerpoint of what they believed was going to be “In” (trending or worth doing) in 2026, and what, conversely, would be out.</p>

<p>Mostly the party was a fun opportunity to gather, make some cheeky jokes at the expense of ourselves and our friends, and lament on the trends we feared would be in…</p>

<p>Here’s my list of trends. Some are personal, some are a little bit of a reflection on society.</p>

<h2 id="out-cellphones-in-the-bedroom">OUT: Cellphones in the bedroom</h2>

<p>Doomscrolling, distractions – some form of these showed up on many peoples’ lists at the party. My practical version: stop bringing cellphones into the bedroom. Remove the possibility for late night scrolling keeping you up too late, or starting your day with the dread of the state of modern politics.</p>

<p>Practically, this has meant plugging our phones in overnight right outside our bedroom door. We get up, we silence the alarms, and we crawl back into bed for a short snooze. But eventually I do get out of bed earlier than I have been, and my phone isn’t always the first thing I gravitate to anymore, once I am up (but, again, it’s only been a week. Hopefully habits won’t die too hard).</p>

<h2 id="in-buying-flowers-for-no-reason">IN: Buying flowers for no reason</h2>

<p>You don’t need a reason to tell someone you love them; you love them, and that’s enough of a reason in and of itself. Surely, then, we don’t need a reason to <em>express</em> that love in a physical and tangible way that delights and brightens one’s day and space.</p>

<p>In that regard, I would propose that buying flowers for your partners, your dates, your friends, is IN for 2026. You don’t need a reason, you don’t need a corporate holiday marred by overpriced flowers, you just need love in your heart, and a desire to express it.</p>

<p>As a couple, we buy flowers with some regularity, and this more just a reminder to continue doing that, and to perhaps inspire friends to as well.</p>

<h2 id="out-github">OUT: GitHub</h2>

<p>2025 saw a lot of tumultuous activity in the United States, for immigrants, minorities, and the under-privileged, at the behest of the latest administration. Spearheading much of the fearmongering and violence is Immigration and Customs Enforcement (ICE). GitHub, <a href="https://github.blog/news-insights/company-news/github-microsoft/">acquired by Microsoft</a> in 2018, have had their fair share of controversy alongside their parent company, including partnering with <a href="https://www.nbcnews.com/tech/tech-news/we-did-not-sign-develop-weapons-microsoft-workers-protest-480m-n974761">weapons manufacturers</a>. But with the recent role of ICE in the state of affairs in the US, GitHub’s <a href="https://www.vox.com/recode/2019/10/9/20906605/github-ice-contract-immigration-ice-dan-friedman">contract with ICE</a>, and the <a href="https://drewdevault.com/2022/06/23/Copilot-GPL-washing.html">pludering of GPL code for Copilot</a>, it’s time for a change.</p>

<p>This past fall I made the move to <a href="https://sourcehut.org">sourcehut</a>, migrating all of my projects, deleting the code from GitHub, and leaving <a href="https://github.com/autoreleasefool/approach">directions for visitors to my larger projects</a>. <a href="https://sr.ht/~sircmpwn/">Drew DeVault</a>, the founder of sourcehut, has his quirks, but he’s firm on privacy, and the value of free and open-source software.</p>

<p>Sourcehut may not be the right move for you, but I promise you there’s a tool that suits your needs out there:</p>

<ul>
  <li>The <a href="https://ziglang.org/news/migrating-from-github-to-codeberg/">Zig programming language</a> moved to the non-profit <a href="https://codeberg.org">Codeberg</a></li>
  <li><a href="https://tangled.org">Tangled</a> offers decentralized git hosting and collaboration</li>
  <li><a href="https://fossil-scm.org/home/doc/trunk/www/index.wiki">Fossil</a> is a weird one, focused on simplicity and self-hosting</li>
</ul>

<h2 id="in-womens-sports">IN: Women’s Sports</h2>

<p>Women’s sports in North America are seeing a boom. The <a href="https://www.thepwhl.com/en/">Professional Women’s Hockey League</a> had their inaugural season in 2023-2024, and has since expanded from 6 teams to 8, including the Vancouver Goldeneyes. If hockey’s not your sport, the <a href="https://www.nsl.ca">Northern Super League</a> also kicked off this year, a professional women’s soccer league recognized by the Canadian Soccer Association.</p>

<p>The gameplay is elite, the women are pros, and as the two leagues get their footing, tickets are significantly cheaper than any mens’ leagues in the city.</p>

<ul>
  <li>This winter check out the <a href="https://www.thepwhl.com/en/teams/vancouver-goldeneyes/tickets/single-game-tickets">Vancouver Goldeneyes</a> at the Pacific Coliseum on the PNE Fairgrounds.</li>
  <li>When the NSL picks back up in April, find the <a href="https://www.nsl.ca/schedule">Vancouver Rise</a> in Burnaby at Swangard Stadium</li>
</ul>

<h2 id="in-personal-blogging">IN: Personal Blogging</h2>

<p>Everybody should have a blog. Not a tumblr, not an premium X account <a href="https://x.com/Write/status/1646674962055565319">with a 10,000 character limit</a>. The Internet has as much breadth as it has depth, and I implore you to share your own</p>

<p>Publish a blog and let me hear your thoughts. <a href="https://addyosmani.com/blog/write-learn/">Writing pushes you to understand topics better</a>, and has numerous other benefits. Talk about what you love, what you fear. Show me your <a href="/posts/vcbf-2025">poetry</a> or impress me with <a href="/posts/10k-covid-time-trial">your personal progress</a>.</p>

<p>Here are a couple blogs I follow diligently, and some recent posts from them I found interesting. Download an RSS Reader and follow your favourites:</p>

<ul>
  <li><a href="https://mothgoth.substack.com">the writer’s room</a> shares her writing and thoughts on her daily life. Check out her recent <a href="https://mothgoth.substack.com/p/the-flesh-pit">queer cosmic horror short story</a></li>
  <li><a href="https://www.jenn.site">Jenn</a> is a mystery to me, but she writes endearingly about all sorts of things, including tech, culture, and her <a href="https://www.jenn.site/dissolution/">dead, deadbeat gay dad</a></li>
  <li><a href="https://daniellemckeirnan.substack.com/">Dead Moms Book Club</a> writes about her life, her grief, her aspirations. <a href="https://daniellemckeirnan.substack.com/p/another-mothers-day">Another Mother’s Day</a> presents a vulnerability I felt in my core.</li>
</ul>

<h2 id="out-the-algorithm">OUT: The Algorithm</h2>

<p>Jenny Odell, in <em>How to Do Nothing: Resisting the Attention Economy</em> writes about her Twitter feed:</p>

<blockquote>
  <p>Scrolling through the feed, I can’t help but wonder: What am I supposed to think of all this? How am I supposed to think of all this? I imagine different parts of my brain lighting up in a pattern that doesn’t make sense, that forecloses any possible understanding. Many things in there seem important, but the sum total is nonsense, and it produces not understanding but a dull and stupefying dread.</p>
</blockquote>

<p>The Algorithm is everywhere, and it’s designed to capture your attention to commoditize it and sell it to the highest bidder. It’s an unfortunate symptom of much of the free Internet, that driving engagement drives ad sales and boosts revenue, and revenue is king.</p>

<p>In 2026, we are casting out the Algorithm and instead embracing RSS and mailing lists. RSS allows you to follow all the interesting people, blogs, news, etc. without needing to be exposed regularly to the whims of The Algorithm.</p>

<p>I have a feed of bloggers, indie devs, and friends that I have curated and can check in on once a day to see if there’s anything new or of note to me posted by them recently. RSS allows me to aggregate these sources and peruse them simply, at my leisure, without needing to traverse bookmarks or keep a hundred tabs open.</p>

<p>My RSS reader of choice is <a href="https://usetapestry.com">IconFactory’s Tapestry for iOS</a>, which also presents me a chronological feed of my Mastodon timeline.</p>

<p>Find creators, writers, comedians, intellectuals elsewhere. I guarantee you they’re posting somewhere outside the walled gardens of social media, where you can follow them on your own terms.</p>

<p>Joan Westenberg writes a little bit more about <a href="https://www.joanwestenberg.com/big-tech-wants-you-trapped-the-open-web-sets-you-free/">ditching big tech in favour of the open web</a>, and how <a href="https://www.joanwestenberg.com/the-illusion-of-independent-thought/">the algorithm has transformed how we form opinions</a>.</p>

<h2 id="to-a-better-year">To a better year</h2>

<p>Every year is an opportunity to invite self-reflection, and consider what’s important to us. 2026, for me, is going to be about dedicating time to the things I love that bring me joy, and pulling myself away from the platforms and media that don’t.</p>]]></content><author><name>Joseph Roque</name></author><category term="personal" /><summary type="html"><![CDATA[Late last year I was invited by a friend to his ‘Ins &amp; Outs’ party for 2026. The basic premise was that everybody would bring a list or powerpoint of what they believed was going to be “In” (trending or worth doing) in 2026, and what, conversely, would be out.]]></summary></entry><entry><title type="html">Sew Far Sew Good</title><link href="https://runcode.blog/2025/12/21/sew-far-sew-good.html" rel="alternate" type="text/html" title="Sew Far Sew Good" /><published>2025-12-21T21:00:00+00:00</published><updated>2025-12-21T21:00:00+00:00</updated><id>https://runcode.blog/2025/12/21/sew-far-sew-good</id><content type="html" xml:base="https://runcode.blog/2025/12/21/sew-far-sew-good.html"><![CDATA[<p>At the November 2025 edition of the <a href="https://growupgames.ca">Grow UP! Game Jam</a>, I teamed up with a group of 6 others game devs to build <a href="https://nielmclaren.itch.io/sew-far-sew-good">Sew Far Sew Good</a>, my first Game Jam game in over 10 years!</p>

<p><a href="https://growupgames.ca">Grow UP!</a> is an in-person game jam hosted locally in Vancouver, that partners with local non-profits to bring awareness to their causes through game jams. I first heard about it during <a href="https://www.fullindie.com">Full Indie</a>’s annual summit, and attended their November iteration.</p>

<p>For their November iteration, they partnered with 2 non-profits:</p>

<ul>
  <li><a href="https://oursocialfabric.ca">Our Social Fabric</a> is a non-profit fabric store that sells donated fabrics and fibre arts supplies, helping keep ‘waste’ fabric out of the landfill. They’ll sell to anyone with an interest in sustainable sewing supplies, including the fibre arts and slow-fashion communities.</li>
  <li><a href="https://spec.bc.ca/waste/repair-cafe/">SPEC Repair Café</a> is a monthly event hosted by the Society Promoting Environmental Conservation, inviting the public to bring their items in need of repair for free support in mending, repairing, and fixing their items. From bikes, to clothing, to computers, they offer free support in cooperation with the City of Vancouver to help keep waste out of landfills.</li>
</ul>

<p><em>An aside: the City of Vancouver has recently cut funding to the SPEC and various sustainability and arts programs by the way of Ken Sim’s ‘Zero means Zero’ budget cratering funding for a wide array of social services, to support a <a href="https://thetyee.ca/News/2025/11/26/Vancouver-Zero-Means-Zero-Budget-Passes/">zero-percent property tax increase and a $50 million budget increase for the Vancouver Police Department</a>. Let city councillors know <a href="https://vancouver.ca/your-government/tell-us-online.aspx">how much this sucks</a> via their website.</em></p>

<p>A week prior to the event, they announced the theme this time around would be <strong>mending</strong>, and, to me, that immediately screamed “sewing”. The first night of the event, Friday, was focused on brainstorming and team building. We did a group brainstorming project, where everybody was contributing to an ideas board, what exactly “mending” meant to them. Once there were lots of ideas percolating, people started to group up to begin collaborating.</p>

<p>I proposed an idea based on my understanding that fabric scissors, for cutting fabrics and textiles, are fragile and prone to nicks when used to cut things other than their intended use. What if we had a game where the goal was to carefully cut and sew patches over holes, trying to ensure you didn’t damage your scissors, while moving speedily to complete the repair? This idea eventually evolved to be the basis of <a href="https://nielmclaren.itch.io/sew-far-sew-good">Sew Far Sew Good</a>.</p>

<p>I ended up with a team of ~7 in total, including 3 programmers, 2 2D artists, a producer, and an SFX artist. Initially, it sounded a bit like there might be too many cooks, but our devs were either new to gamedev (me) or new to the tooling, meaning we’d be focused on building something fairly small scoped, and hopefully wouldn’t trample on each other’s work too much. Our 2D artists, producer, and SFX artist also found themselves collaborating with multiple teams, or working on their own games on the side, and I think that made for a nice balance, where everybody could find time to contribute to Sew Far Sew Good, and otherwise focus on their own thing.</p>

<p>Overall, the weekend went by quickly, with our team regrouping Saturday morning to kick off game development. By early Saturday, we hadn’t actually fully settled on a game to build… Those of us that arrived earliest started familiarizing ourselves with the tooling of Godot, and just generally working on what we wanted, not fully committed to collaborating. When our producer showed up, they brought up some of the ideas we had had the night before, and suggested we settle on one that seemed approachable, tightly scoped, and fun enough to play. A new Godot project was created, and we were off.</p>

<p>My focus in the game was on the character’s movements. I worked on keyboard controls, as well as programming the animations one of our artists delivered. Looking back, the work was really simple in the end, and the code reflects that. But with it being my first real foray into Godot, it took me most of Saturday to get just a basic character walking animation working!</p>

<p><img src="/assets/posts/sew-far-sew-good-character-process.png" alt="A screenshot of code. A single function called 'process' containing about 25 lines of if-else statements to set an animation variable based on the current state." /></p>

<figcaption>A screenshot of code. A single function called 'process' containing about 25 lines of if-else statements to set an animation variable based on the current state.</figcaption>

<p>This screenshot constitutes most of Saturday’s work! As you can see, besides some other variables being set by signals elsewhere in the code, it isn’t very complex at all. But bumbling through this was a fun challenge perfectly sized for my absolutely beginner Godot skills.</p>

<p><img src="/assets/posts/sew-far-sew-good-cutting.png" alt="A screenshot of the game. A small yellow character struggles to push a large pair of scissors around a heard shaped cutout. A time counts down with 57 seconds remaining." /></p>

<figcaption>A screenshot of the game. A small yellow character struggles to push a large pair of scissors around a heard shaped cutout. A time counts down with 57 seconds remaining.</figcaption>

<p>The team managed to get a solid demo by Saturday night, and most of Sunday was spent refining, and expanding. We added a couple more shapes to our game, we cleaned up the menus, and I worked on an algorithm to generate sewing points around the patches to randomize the gameplay a little bit.</p>

<p><img src="/assets/posts/sew-far-sew-good-sewing.png" alt="A screenshot of the game. The small yellow character stands above a heart shaped patch, with lines representing thread zigzagging between the patch and the background. A timer counts down with 14 seconds remaining." /></p>

<figcaption>A screenshot of the game. The small yellow character stands above a heart shaped patch, with lines representing thread zigzagging between the patch and the background. A timer counts down with 14 seconds remaining.</figcaption>

<p>By the end of Sunday, I’m proud to say, we had a fully playable + complete game! With 3 different shapes, and an overall accuracy report at the end of the game.</p>

<p>Overall, I’m really satisfied with what we accomplished. We managed to scope out a simple game perfectly sized for the weekend and skills that we had as a team. We collaborated effectively, finding things for everybody to do, and helping each other when we got stuck on a tricky bit of code, or tough interaction.</p>

<p>Despite feeling a bit overwhelmed at the start of the weekend, by Sunday I was getting really comfortable with Godot. It’s been an incredible tool, and was so easy to learn thanks to their detailed documentation.</p>

<p>You can play the game <a href="https://nielmclaren.itch.io/sew-far-sew-good">here</a>.</p>]]></content><author><name>Joseph Roque</name></author><category term="game-dev" /><category term="godot" /><category term="gdscript" /><category term="game-jam" /><category term="2d" /><category term="grow-up-games" /><summary type="html"><![CDATA[At the November 2025 edition of the Grow UP! Game Jam, I teamed up with a group of 6 others game devs to build Sew Far Sew Good, my first Game Jam game in over 10 years!]]></summary></entry><entry><title type="html">VCBF: 2025 Haiku Invitational</title><link href="https://runcode.blog/2025/10/30/vcbf-2025.html" rel="alternate" type="text/html" title="VCBF: 2025 Haiku Invitational" /><published>2025-10-30T00:20:00+00:00</published><updated>2025-10-30T00:20:00+00:00</updated><id>https://runcode.blog/2025/10/30/vcbf-2025</id><content type="html" xml:base="https://runcode.blog/2025/10/30/vcbf-2025.html"><![CDATA[<p>The Vancouver Cherry Blossom Festival has <a href="https://vcbf.ca/winning_haiku/2025-winning-haiku/">announced its winners</a> for 2025’s Haiku Invitational. I was excited to see this year I was awarded a “Sakura Award”! Still hoping to win some year 🤞</p>

<h2 id="poem-1-sakura-award">Poem 1 (Sakura Award)</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cherry petals  
find a home in my hair  
where do I belong?
</code></pre></div></div>

<h2 id="poem-2">Poem 2</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Six feet underground
Are the cherry blossoms
Still blooming?
</code></pre></div></div>]]></content><author><name>Joseph Roque</name></author><category term="poetry" /><category term="writing" /><category term="vcbf" /><summary type="html"><![CDATA[The Vancouver Cherry Blossom Festival has announced its winners for 2025’s Haiku Invitational. I was excited to see this year I was awarded a “Sakura Award”! Still hoping to win some year 🤞]]></summary></entry><entry><title type="html">10 Years of Building Approach for Android and iOS</title><link href="https://runcode.blog/2025/04/01/10-years-of-approach.html" rel="alternate" type="text/html" title="10 Years of Building Approach for Android and iOS" /><published>2025-04-01T10:00:00+00:00</published><updated>2025-04-01T10:00:00+00:00</updated><id>https://runcode.blog/2025/04/01/10-years-of-approach</id><content type="html" xml:base="https://runcode.blog/2025/04/01/10-years-of-approach.html"><![CDATA[<p>Exactly 10 years ago I published the first version of my first solo Android app, the 5 Pin Bowling Companion. A lot has changed over the years, and I continue to maintain it today. Multiple total rewrites, a Kotlin migration, and an iOS release, I’m going to talk a little about the journey this app has been through.</p>

<h2 id="why-5-pin-bowling">Why 5-Pin Bowling?</h2>

<p>I started working on the first version of the Bowling Companion (its original name!) in January 2015. I was in my 2nd year of university, and was going to have to start applying to co-op positions for that summer. I was looking for my niche, something that I could build that would be unique, that I had a need for, and that I would be passionate about building. I had been bowling most of my life at that point, and realized that there wasn’t really a good option for 5-Pin Bowling scorekeepers on Android. Actually, I can’t remember if there were any at all! So I set my sights on building the first.</p>

<h2 id="the-5-pin-bowling-companion-the-java-era">The 5-Pin Bowling Companion: The Java Era</h2>

<h3 id="version-one-functional-but-ugly">Version One: Functional, but Ugly</h3>

<p>I built the first version of the app over the course of about a month. I focused primarily on the functionality – things like data storage, score calculations, and statistics. Back then the Bowling Companion was written in Java, with XML, Views, and SQL. Every screen the app contained was a separate Activity, statistics were collected and calculated in an <code class="language-plaintext highlighter-rouge">AsyncTask</code> within the <code class="language-plaintext highlighter-rouge">StatsActivity</code>, and the UI was not quite fleshed out…</p>

<p>One thing that was good enough about this first version though? It helped me stand out among other co-op candidates, and I landed my first internship as an Android developer thanks to being one of the only ones with actual Android experience!</p>

<p><img src="/assets/posts/10-years-of-approach-android-v0.1.png" alt="v0.1 of the 5 Pin Bowling Companion, circa February 2015" /></p>

<figcaption>v0.1 of the 5 Pin Bowling Companion, circa February 2015</figcaption>

<h3 id="version-two-more-functional-less-ugly">Version Two: More Functional, Less Ugly</h3>

<p>Once I finished the first version, and had it functional, I made the most logical decision – scrap it and rebuild it from scratch!</p>

<p>I figured at that point, I had learned so much in the past month, from what APIs were available, to how to better structure my code, that I had already built up so much technical debt, and the performance was likely so poor I could easily make major improvements. So I copied the existing code over to a new folder, and created a new Android template from scratch.</p>

<p>Since the app was mostly working the way I wanted it to at this point, I focused on the design. You can see there are some pretty significant changes in the look of the app from v0.1 to v0.2.</p>

<p><img src="/assets/posts/10-years-of-approach-android-v0.2.png" alt="v0.2 of the 5 Pin Bowling Companion, circa March 2015" /></p>

<figcaption>v0.2 of the 5 Pin Bowling Companion, circa March 2015</figcaption>

<h3 id="version-three-the-initial-release">Version Three: The Initial Release</h3>

<p>Once I had the app looking pretty well how I wanted to, trying out this new “Material” thing Google was toting, I decided that I had, once again, come so far in my learning, it was time to scrap it and start all over once again. This time around, I focused on performance, functionality, and code structure.</p>

<p>I referred to the past implementations a lot, but starting from scratch again allowed me to easily make the changes I needed to, without having to worry about compatibility with what I had already written. Much of the underlying code, like the database, remained the same, but the entire structure of the app changed. I moved from Activities to Fragments, and pulled a lot of the scoring logic out of the activities into utility functions.</p>

<p>This rewrite would eventually become the first version launched, on April 1st, 2015.</p>

<p><img src="/assets/posts/10-years-of-approach-android-v1.0.png" alt="v1.0 of the 5 Pin Bowling Companion, circa April 2015" /></p>

<figcaption>v1.0 of the 5 Pin Bowling Companion, circa April 2015</figcaption>

<h3 id="early-lessons-from-the-early-days">Early Lessons from the Early Days</h3>

<ol>
  <li><strong>Kill your darlings</strong>. I wanted to be proud of this, I wanted to share it with a community I was a part of, and I wanted to provide something useful. The first version of the app, while functional, was not a delight to use! Redesigning it on top of the existing code would probably have taken me much longer, and I might not have gotten that first version out for months, with a lot more technical debt.</li>
  <li><strong>Think about your data model</strong>. The biggest mistake I made in these early days was being far too restrictive with the SQL constraints and datatypes. It made refactors difficult, when I better understood how I should be formatting the data and defining relationships. Some features were impossible for a while until I restructured the database! Taking the time to think about your long-term data model, and leaving yourself some wiggle room in the future can be extremely useful, before you lock yourself into something that becomes impossible to build on top of.</li>
</ol>

<h2 id="rewriting-in-kotlin">Rewriting in Kotlin</h2>

<p>In February 2018, not too long after <a href="https://techcrunch.com/2019/05/07/kotlin-is-now-googles-preferred-language-for-android-app-development/">Google announced</a> official support for Kotlin in Android Studio, I decided to rewrite the Bowling Companion in Kotlin. I experimented with the language a bit before diving in, and found that I really liked a lot of its early ergonomics especially when compared with Java. At this point I just recently graduated and was returning to working at Shopify on their native (at the time) Swift app, and I liked that Kotlin reminded me a lot of Swift.</p>

<p>The rewrite would end up taking me about 7 months, starting in February, with the first Kotlin version being released in September 2018. I don’t fully remember the process, but I do remember that I did not do it component-by-component, instead opting to fully rewrite the app from scratch and convert it all at once. Probably not the best idea, especially since up until this point I had never written a single unit test for the app, but it was hardly the app’s first rewrite! But the Bowling Companion has always first and foremost been a playground for me to explore in, and this was just another exploration.</p>

<p>In writing this blog post, I found the first couple commits, which I think are pretty comical – <a href="https://git.sr.ht/~autoreleasefool/approach/tree/b83a1d5d4f0d3b1dc794f8e94d0be3696acc8fee">the first commit</a>, a complete deletion of the entire Java source code, and <a href="https://git.sr.ht/~autoreleasefool/approach/tree/ebaf0a33bf570fa0f0a6551d3a5350e9351f8ac3">the second commit</a>, a “Hello World!” Kotlin template. Then, in September, the <a href="https://git.sr.ht/~autoreleasefool/approach/tree/72c75cfe55d7610956d250d348c6af1b997eda2e">3.0.0 Release</a> of the Kotlin rewrite.</p>

<p>The months following that release I was plagued with crash reports from <a href="https://www.bugsnag.com/">BugSnag</a>. Mostly <code class="language-plaintext highlighter-rouge">IllegalStateException</code>s, but there was quite a variety. I chased those bugs down and fixed what I could. I don’t have a ton of recollection of this time, I’m mostly just looking through GitHub Issues to jog my memory.</p>

<p><img src="/assets/posts/10-years-of-approach-android-v3.0.png" alt="v3.0 of the 5 Pin Bowling Companion, circa September 2018" /></p>

<figcaption>v3.0 of the 5 Pin Bowling Companion, circa September 2018</figcaption>

<h2 id="taking-a-break">Taking a Break</h2>

<p>In 2020, I ended up mostly taking a break from working on the Bowling Companion. There were few, if any, bug reports or feature requests coming in, and I had picked up some other projects. I was mostly content with where the app was at. I had removed the ads and Google analytics, as I in my mind I didn’t want there to be any burden to maintain it – the app was completely free and detached, and therefore I offered it as-is. I would work on it when I wanted to, on my own terms.</p>

<p>The last version I ended up releasing of this iteration of the app was <code class="language-plaintext highlighter-rouge">3.2.0</code>, in August 2020, targetting SDK 28.</p>

<h2 id="approach-for-ios">Approach for iOS</h2>

<p>In 2022, I started working on the first version of the Bowling Companion for iOS. Mostly I was looking for a new native iOS Swift project to work on, while I was working with React Native in my job. I had recently learned about <a href="https://github.com/pointfreeco/swift-composable-architecture">The Composable Architecture</a>, had some experience building with SwiftUI, and decided to go all in on both for this app.</p>

<p>With how old the Android app had become at this point, I knew I wanted to match its featureset, but I wouldn’t be using its design for any inspiration. I relied on SwiftUI’s default styling primarily, and built an all new UI for the app. This also gave me a chance to rethink the structure of the app from a UX perspective, moving some content to top-level tabs, but leaving the hierarchical nature of Bowlers &gt; Leagues &gt; Series &gt; Games as the primary way users would interact with the app.</p>

<p>Overall, the Bowling Companion for iOS would take me just under a year to build. It wasn’t until March of 2023 that I settled on the name “Approach”, and the first version of the iOS app would release in September 2023.</p>

<p><img src="/assets/posts/10-years-of-approach-ios-v1.0.png" alt="v1.0 of Approach for iOS, circa September 2023" /></p>

<figcaption>v1.0 of Approach for iOS, circa September 2023</figcaption>

<h2 id="jetpack-compose-and-building-approach-for-android">Jetpack Compose and building Approach for Android</h2>

<p>During the process of building Approach for iOS, Google announced that they would be removing unsupported apps from the Play Store, and any app that wasn’t targeting at least SDK 32 (<em>I think</em>) would become unavailable to new users. Of course, as mentioned above, the Bowling Companion only targeted SDK 28, and quite a bit had happened in the ~4 years since its release.</p>

<p>Some of the major hurdles I encountered when thinking about how to address this were:</p>

<ul>
  <li>Kotlin, and especially Kotlin’s Coroutines, had undergone a major version update with breaking changes</li>
  <li>Views were no longer the default recommended by Google, and Jetpack Compose was now preferred</li>
  <li>The Charts library I was using, <a href="https://github.com/PhilJay/MPAndroidChart">MPAndroidChart</a> had not been updated in some time and was likely abandoned.</li>
</ul>

<p>Once again, I would lean on my trusty tool, <strong>the rewrite</strong>. This would now be the SIXTH time I had written the Bowling Companion from scratch, leaning on years of lessons and inspiration, to create what I think is by and far the best version of the app that’s ever existed.</p>

<p>This rewrite ended up taking just over a year to complete, and Approach for Android was first released in September 2024, with feature parity to the iOS app!</p>

<p><img src="/assets/posts/10-years-of-approach-android-v4.0.png" alt="v4.0 of Approach for Android, circa September 2024" /></p>

<figcaption>v4.0 of Approach for Android, circa September 2024</figcaption>

<h2 id="current-state-of-affairs">Current State of Affairs</h2>

<p>As of writing (April 2025), Approach is two native apps, for iOS and Android. Features are generally built in parity, and released in unison!</p>

<p>The iOS app is built with <a href="https://github.com/pointfreeco/swift-composable-architecture">TCA</a> and <a href="https://developer.apple.com/xcode/swiftui/">SwiftUI</a>. It supports iOS 17 and 18. There are 138 modules, including 36 feature modules, 15 repositories, 19 services, and 34 libraries.</p>

<p>The Android app is built with <a href="https://developer.android.com/compose">Jetpack Compose</a>, MVVM, and follows Google’s recommended (as of 2025) layered architecture. It supports SDKs 26 and higher, and targets SDK 35. There are 92 modules, including 20 core modules, 36 feature modules, and 36 feature UI modules.</p>

<p>In total, in the <a href="https://git.sr.ht/~autoreleasefool/approach">repository</a> there have been 3,986 commits to <code class="language-plaintext highlighter-rouge">main</code>, averaging just over 1 commit per day! From a clean clone, <a href="https://github.com/AlDanial/cloc">cloc</a> reports 65,526 lines of Swift across 902 files and 66,347 lines of Kotlin across 901 files. In total, it reports 161,776 lines of “code”, but that includes SVGs, some generated localizations, and some template files.</p>

<p><img src="/assets/posts/10-years-of-approach-languages.png" alt="Approximate percentage of the codebase which is Swift vs Kotlin. Swift shows 49.9%, Kotlin is 49.3%, and &quot;Other&quot; is 0.8%" /></p>

<figcaption>Approximate percentage of the codebase which is Swift vs Kotlin. Swift shows 49.9%, Kotlin is 49.3%, and "Other" is 0.8%</figcaption>

<p>See you in another 10 years! Maybe I’ll be talking about <a href="https://android-developers.googleblog.com/2024/05/android-support-for-kotlin-multiplatform-to-share-business-logic-across-mobile-web-server-desktop.html">Kotlin Multiplatform</a>, <a href="https://skip.tools/">Skip</a>, or something as of yet unreleased!</p>]]></content><author><name>Joseph Roque</name></author><category term="approach" /><category term="ios" /><category term="android" /><category term="bowling" /><summary type="html"><![CDATA[Exactly 10 years ago I published the first version of my first solo Android app, the 5 Pin Bowling Companion. A lot has changed over the years, and I continue to maintain it today. Multiple total rewrites, a Kotlin migration, and an iOS release, I’m going to talk a little about the journey this app has been through.]]></summary></entry><entry><title type="html">Development Log, January 2025</title><link href="https://runcode.blog/2025/02/19/dev-log-2025-01.html" rel="alternate" type="text/html" title="Development Log, January 2025" /><published>2025-02-19T08:00:00+00:00</published><updated>2025-02-19T08:00:00+00:00</updated><id>https://runcode.blog/2025/02/19/dev-log-2025-01</id><content type="html" xml:base="https://runcode.blog/2025/02/19/dev-log-2025-01.html"><![CDATA[<p>December was a month of coding challenges, as I took on <a href="https://adventofcode.com/">Advent of Code</a>, and January was a slow month of vacationing in Uruguay, then reintegrating into the real world.</p>

<h2 id="advent-of-code">Advent of Code</h2>

<p>Every year since its inception I’ve participated in <a href="https://adventofcode.com/">Advent of Code</a>, an annual advent calendar of coding challenges that get released throughout the month of December, with the eventual goal of completing each day and saving Christmas.</p>

<p>Challenges are released each night at 12AM Eastern Time, meaning that I get them at a cool 9PM Pacific. Most nights I hope on a call with some friends and we chat about past challenges and when we’ll encounter certain theories or formulas that have been the hardest challenges in past years.</p>

<p>This year, I ended up with a total of 40/50 stars (challenges solved) at the end of December.</p>

<p>Of all the challenges this year, I think the one I had the most fun with was Day 14, simulating the movement of robots, and finding when they arrange themselves in a Christmas tree shape. You can find my code <a href="https://git.sr.ht/~autoreleasefool/advent-of-code/tree/main/item/2024/day_14/swift/Day14.swift">here</a> which was to basically just continue the simulation and search for a bunch of robots lining up in a row. How I actually solved it realtime though was printing every state of the simulation to my terminal and just using Cmd+F to search for an unlikely number of robots in a single row – I think it was ~10 or so, and it took me straight to the state that output the tree! I ended up 324th overall that day, one of my best performances this year.</p>

<h2 id="hot-chocolate-fest">Hot Chocolate Fest</h2>

<p>Every January in Vancouver Hot Chocolate Fest begins. Their website is always a little difficult to navigate, and it’s never easy to find nearby participating cafés with good flavours. So this year I gave myself a fun little challenge – 3 days to build a better app than they had. <a href="https://git.sr.ht/~autoreleasefool/hot-chocolate-fest">Here’s the final result</a>.</p>

<p>First, scraping the data. The website is annoying to navigate and, so too, was the data source. I ended up scraping 2 pages – the first for the vendor names and details of the hot chocolates they were selling, and the second for the vendor locations from a custom Google Map.</p>

<p>With the data scraped and cleaned up into convenient JSONs for me to read, I began building an interface. I chose SwiftUI simply because I have familiarity with it, it’s quick for bootstrapping, and I wanted this done fast. I also chose SwiftData for the little data I wanted to store alongside the scraped data. This ended up being a mistake – I’ve never used SwiftData personally, and it wasn’t intuitive to map the data I had to the storage format I wanted/SwiftData expected. In the end, I switched over to Pointfree’s <a href="https://github.com/pointfreeco/swift-sharing">Sharing</a> library, which simplified things a lot.</p>

<p>The end result was a simple little app that listed all the vendors and flavours, with a search by name, or a filter by allergens, as well as a map with details of the vendors and offerings. Unfortunately, Hot Chocolate Fest only lasts 1 month and I started this project halfway into the event. Hopefully I can dust it off next January and use it then!</p>]]></content><author><name>Joseph Roque</name></author><category term="dev-log" /><category term="swift" /><category term="programming" /><category term="advent-of-code" /><summary type="html"><![CDATA[December was a month of coding challenges, as I took on Advent of Code, and January was a slow month of vacationing in Uruguay, then reintegrating into the real world.]]></summary></entry><entry><title type="html">Development Log, November 2024</title><link href="https://runcode.blog/2024/12/03/dev-log-2024-11.html" rel="alternate" type="text/html" title="Development Log, November 2024" /><published>2024-12-03T08:00:00+00:00</published><updated>2024-12-03T08:00:00+00:00</updated><id>https://runcode.blog/2024/12/03/dev-log-2024-11</id><content type="html" xml:base="https://runcode.blog/2024/12/03/dev-log-2024-11.html"><![CDATA[<p>This month I wrote a meager 4,251 words for <a href="https://nanowrimo.org/">NaNoWriMo</a>, added a few new statistics to the Approach app, and started prepping for <a href="https://adventofcode.com/">Advent of Code</a>.</p>

<h2 id="nanowrimo">NaNoWriMo</h2>

<p>As I do every year, I attempted to complete NaNoWriMo this November. The goal of NaNoWrimo (National Novel Writing Month) is to write 50,000 words over the course of a month, averaging 1,667 words per day, on one’s way to finishing the first draft of a novel.</p>

<p>I’ve always loved writing, though I generally stick to much more short form writing, but I still aspire to write a novel someday. NaNoWriMo humbles me every year, as I realize just how much of a commitment it is, and, more than that, how hard it is to write something good!</p>

<p>I do enjoy the few writing sessions I get out of it each year though. I generally have some semblance of an idea going in, and then after a few days I lose interest in that idea and move on. I’m still searching for the novel idea that’ll make me want to keep writing.</p>

<h2 id="approach">Approach</h2>

<p><a href="https://tryapproach.app">Approach</a> is the 5 Pin Bowling app I build for iOS and Android. This month I didn’t spend to much time on it, instead preferring to focus on Advent of Code, but I did finish a few small tasks.</p>

<h3 id="adding-high-series-of-x-statistics">Adding High Series of X Statistics</h3>

<p>One of the most useful things about Approach is the statistics it offers, reporting on a variety of aspects about your bowling and you’re improving or where you need to focus your game. These statistics can be charted as a series of points, or accumulated over time to a singular value.</p>

<p>Approach has a system for calculating the statistics, which queries the user’s games, and passes the games to a set of objects which perform calculations based on the properties of the game, and return the final statistic values. This makes it fairly quick and simple to add new statistics into the app.</p>

<p>This month, a user noted that there’s a “High Series of 3” statistic, tracking the highest total series the user has bowled for a series of 3 games. But often leagues are 4 or even more games, and there was no way to measure improvements in those leagues.</p>

<p>Therefore, this month I added a number of statistics for “High Series” of 2 up to 20 games.</p>

<h2 id="advent-of-code">Advent of Code</h2>

<p>December 1st marks the beginning of <a href="https://adventofcode.com/">Advent of Code</a>, a month-long coding challenge that releases a new puzzle at midnight EST each night, where coders all over the world attempt to parse the puzzle and determine a solution as quickly as possible.</p>

<p>I’ve been doing Advent of Code since it first launched in 2015, and I’ve built up a small library of helpful utilities over those years. However, they’re all written in Python because I used to use Python a lot more back in those days, for work or personal projects. But I rarely write Python at all anymore. In fact, the last time I wrote any Python was actually during last year’s Advent of Code. I’ve found that it leads to me spending too much time remembering the intricacies of Python’s list comprehension, or the syntax and tools of my own library.</p>

<p>This year, I’ve decided to attempt to solve all the days using Swift, my preferred language of choice. I spent some time in November converting my personal CLI tool for running the challenges from Python to Swift, and have also converted a number of my personal utilities over to Swift as well.</p>

<p>I post all my Advent of Code <a href="https://git.sr.ht/~autoreleasefool/advent-of-code">solutions</a> on GitHub, and this year will be no different!</p>

<p>I’ll have more of a write-up on how it goes at the end of the month!</p>]]></content><author><name>Joseph Roque</name></author><category term="dev-log" /><category term="approach" /><category term="ios" /><category term="swift" /><category term="programming" /><category term="advent-of-code" /><category term="writing" /><summary type="html"><![CDATA[This month I wrote a meager 4,251 words for NaNoWriMo, added a few new statistics to the Approach app, and started prepping for Advent of Code.]]></summary></entry><entry><title type="html">Development Log, October 2024</title><link href="https://runcode.blog/2024/10/31/dev-log-2024-10.html" rel="alternate" type="text/html" title="Development Log, October 2024" /><published>2024-10-31T17:00:00+00:00</published><updated>2024-10-31T17:00:00+00:00</updated><id>https://runcode.blog/2024/10/31/dev-log-2024-10</id><content type="html" xml:base="https://runcode.blog/2024/10/31/dev-log-2024-10.html"><![CDATA[<p>This month I attended the Vancouver Writer’s Fest in anticipation of NaNoWriMo, and built some data management tooling for Approach.</p>

<h2 id="vancouver-writers-fest">Vancouver Writer’s Fest</h2>

<p>This month was Vancouver’s annual Writer’s Fest, and I finally decided to attend some of the events. I always take a look at the festival’s events and am never really in too much of an active writing mood, but this year I decided to make sure I made it more of a priority and to go to at least one event.</p>

<p>In the end, I actually ended up going to 4!</p>

<p>The most useful was, I think, <strong>Night Class: A Workshop with UBC School of Creative Writing</strong>, where various UBC professors of their creative writing department came to give very condensed lectures and then a short writing prompt. My biggest takeaway from this event was one of the professors (I think it was Whitney French, though I might be misremembering) talked about for her to really get into writing, she had to figure out how her brain worked. For me, I definitely have trouble choosing and focusing on a single task. What works for me when I’m coding is that I let myself pick up and drop tasks very quickly – short, little bursts of intense focus, and then drop it and switch to something new, until I find something that really catches me and I can dedicate time and deep focus to it. Sometimes I have to pick up and drop the same thing multiple times before I can really take off with it. I’ve never applied this style of thinking and working to my writing. I always try to pick one project and see it through for a while before I drop it and never return to it, usually even dropping writing altogether for weeks or months at a time. I’m going to try some different approaches this November to see if I can coax some better writing out of myself this month.</p>

<h2 id="nanowrimo">NaNoWriMo</h2>

<p>Speaking of November, <a href="https://nanowrimo.org/">NaNoWriMo</a> is kicking off next month, an annual challenge to write 50,000 words and finish the first draft of a novel. It’s much less about writing anything of substance, and mostly just about volume – writing and building the habit of writing!</p>

<p>I’ll be doing little to no coding for the next month (if I can keep myself honest…) and I’ll be focused on writing instead.</p>

<h2 id="approach">Approach</h2>

<p><a href="https://tryapproach.app">Approach</a> is the 5 Pin Bowling app I build for iOS and Android. This month I focused on iOS and data management.</p>

<h3 id="automatic-backups">Automatic Backups</h3>

<p>Approach for iOS uses <a href="https://github.com/groue/GRDB.swift">GRDB</a>, a framework for working with SQLite from Swift that makes it really simple to query your SQLite DB with strong typing and robust error handling and concurrency. Perfect for Swift.</p>

<p>Unfortunately, one issue I’ve had with GRDB is figuring out how to safely and effectively sync the database to iCloud. Approach doesn’t charge any money or have a premium plan (yet) and I’m not quite willing to front the costs for a fully fledged backend sync, so it’s gone without any sort of cloud syncing up until now. There’s an option for users to export their data to keep it safe, but I’m not sure you can rely on non-technical users to do that (especially the aging demographic of bowlers).</p>

<p>My solution? I eventually realized that SQLite conveniently works out of a single file. All the data is stored there, and we can move or copy that file easily to create a backup! So all I had to do is use GRDB’s built in <a href="https://github.com/groue/GRDB.swift/tree/master?tab=readme-ov-file#backup">backup</a> command to copy the database to another file, then I could sync that file to a folder in the user’s iCloud (as long as they’re signed into an Apple account).</p>

<p><img src="/assets/posts/devlog-2024-10-approach-ios_backups.png" alt="Screenshot of an iPhone. The title of the screen is &quot;Backups&quot;. There is a section describing backups, a toggle to enable or disable them, and a list of recent backup dates." /></p>

<figcaption>Screenshot of an iPhone. The title of the screen is "Backups". There is a section describing backups, a toggle to enable or disable them, and a list of recent backup dates.</figcaption>

<p>Backup syncing will be enabled by default, but users will be able to disable it and delete the files from their iCloud if they can’t spare the room. It’ll backup with some regularity, and we’ll make sure the backups don’t get too stale or take up too much room. That’s all still a work in progress, but coming soon!</p>

<h3 id="imports-from-android">Imports from Android</h3>

<p>I had a user reach out mid-October and ask why they couldn’t get their data to import correctly from the exported file from their Samsung phone, onto their new iPhone. The reason was that I had never actually built support for this! Let’s understand why this was the case, and how I built support for it.</p>

<h4 id="ios-data-format">iOS Data Format</h4>

<p>When using GRDB’s automatic encoding and decoding for records, column names end up matching exactly how you define the corresponding variable (for example, a League belongs to a Bowler through the <code class="language-plaintext highlighter-rouge">bowlerId</code> foreign key, which is GRDB will write the column name). You can overwrite this default by explicitly defining <code class="language-plaintext highlighter-rouge">CodingKeys</code> on a <code class="language-plaintext highlighter-rouge">Codable</code> record, but I opted to use the default behaviour.</p>

<p><img src="/assets/posts/devlog-2024-10-approach-ios_league_column_names.png" alt="Screenshot of two adjacent windows. The first has a Swift struct definition, League, and a list of properties. The bowlerId property is highlighted. The second has a SQLite table definition. The bowlerId column name is highlighted" /></p>

<figcaption>Screenshot of two adjacent windows. The first has a Swift struct definition, League, and a list of properties. The bowlerId property is highlighted. The second has a SQLite table definition. The bowlerId column name is highlighted</figcaption>

<p>Another default, <code class="language-plaintext highlighter-rouge">enum</code>s are encoded as their String representation, so the enum <code class="language-plaintext highlighter-rouge">League.ExcludeFromStatistics</code> has two cases, <code class="language-plaintext highlighter-rouge">include</code> and <code class="language-plaintext highlighter-rouge">exclude</code> and the SQLite values reflect that:</p>

<p><img src="/assets/posts/devlog-2024-10-approach-ios_enum_values.png" alt="Screenshot of two adjacent windows. The first has a Swift enum definition, Recurrence, and has two values, 'repeating' and 'once'. The values are highlighted. The second has a SQLite table definition. The recurrenc column values matching the enum values are highlighted" /></p>

<figcaption>Screenshot of two adjacent windows. The first has a Swift enum definition, Recurrence, and has two values, 'repeating' and 'once'. The values are highlighted. The second has a SQLite table definition. The recurrenc column values matching the enum values are highlighted</figcaption>

<h4 id="android-data-format">Android Data Format</h4>

<p>Approach for Android uses <a href="https://developer.android.com/training/data-storage/room">Room</a>, the de facto data storage solution for the platform. It has slightly different defaults.</p>

<p>When defining your models, you can easily override the column names directly inline, otherwise they will match your variable definition. This led me to mix the Kotlin standard camelCase with SQLite standard snake_case names, appropriate for each platform. A League’s <code class="language-plaintext highlighter-rouge">bowlerId</code> property in the Kotlin model would be reflected as <code class="language-plaintext highlighter-rouge">bowler_id</code> in the SQLite table column:</p>

<p><img src="/assets/posts/devlog-2024-10-approach-android_league_column_names.png" alt="Screenshot of two adjacent windows. The first has a Kotlin data class definition, League, and a list of properties. The bowlerId property is highlighted. The second has a SQLite table definition. The bowler_id column name is highlighted" /></p>

<figcaption>Screenshot of two adjacent windows. The first has a Kotlin data class definition, League, and a list of properties. The bowlerId property is highlighted. The second has a SQLite table definition. The bowler_id column name is highlighted</figcaption>

<p>In Kotlin, enum class values are usually defined in UPPERCASE, and the value stored in SQLite reflects that:</p>

<p><img src="/assets/posts/devlog-2024-10-approach-android_enum_values.png" alt="Screenshot of two adjacent windows. The first has a Kotlin enum class definition, Recurrence, and has two values, 'REPEATING' and 'ONCE'. The values are highlighted. The second has a SQLite table definition. The recurrenc column values matching the enum values are highlighted" /></p>

<figcaption>Screenshot of two adjacent windows. The first has a Kotlin enum class definition, Recurrence, and has two values, 'REPEATING' and 'ONCE'. The values are highlighted. The second has a SQLite table definition. The recurrenc column values matching the enum values are highlighted</figcaption>

<h4 id="supporting-imports">Supporting Imports</h4>

<p>With these platform differences in mind (among others), I couldn’t simply swap out the underlying DB file when importing, so I had to take a slightly custom approach.</p>

<p>First, I let the user pick the file from their filesystem. iOS will give you a URL as a result, and I start by copying that file to a temporary location (that I clean up at the end of the process) so the system can’t lock my access or delete it out from underneath me while I’m trying to process the file.</p>

<p>Next, I look at the <a href="https://git.sr.ht/~autoreleasefool/approach/tree/main/item/ios/Approach/Sources/ImportExportService/Models/FileType.swift">file extensions and headers</a> and try to determine what sort of file is being imported. When exporting, Approach for iOS uses GRDB’s <a href="https://github.com/groue/GRDB.swift/tree/master?tab=readme-ov-file#backup">backup</a> function to cleanly write to a fresh SQLite DB File. Room doesn’t expose this as easily, so instead I zip up the <code class="language-plaintext highlighter-rouge">sqlite</code> and <code class="language-plaintext highlighter-rouge">sqlite-wal</code> files and give the user a single zip file. That means when importing I need to know if I’m dealing with a SQLite file or a ZIP file and act accordingly. Other files are unsupported and the app will report an error to the user.</p>

<p>Once I have the primary SQLite file, I open it with GRDB and try to <a href="https://git.sr.ht/~autoreleasefool/approach/tree/main/item/ios/Approach/Sources/ImportExportService/Models/DatabaseFormat.swift">perform a few reads</a> to see if I can determine the format. GRDB uses migrations and a <code class="language-plaintext highlighter-rouge">grdb_migrations</code> table to track which migrations have been applied. I know that I can import any Approach for iOS file that has no newer migrations than the current app has – any older and GRDB will automatically handle the migration, any newer and the database is in a format we can’t understand. <a href="https://git.sr.ht/~autoreleasefool/approach/tree/main/item/android/core/database/schemas/ca.josephroque.bowlingcompanion.core.database.ApproachDatabase/5.json">Room uses a hash value</a> to define its DB versions, so I can inspect those.</p>

<p>For any supported iOS DB versions, I just <a href="https://git.sr.ht/~autoreleasefool/approach/tree/main/item/ios/Approach/Sources/ImportExportService/Importing/IOSApproachSQLiteImporter.swift">copy the file</a> to Approach for iOS’s DB file location and let GRDB handle the import/migration. For supported Android DB versions, I perform a series of queries and writes to ensure all the data gets copied over correctly. These queries will have to be kept up to date as the database format evolves over time, but that’s a trade-off I was okay with!</p>

<p>Once the DB file has been prepared, we attempt to open it and if it all succeeds, we let the user inspect the app and validate everything imported correctly – there is always an option to revert.</p>]]></content><author><name>Joseph Roque</name></author><category term="dev-log" /><category term="approach" /><category term="ios" /><category term="swift" /><category term="programming" /><category term="writing" /><summary type="html"><![CDATA[This month I attended the Vancouver Writer’s Fest in anticipation of NaNoWriMo, and built some data management tooling for Approach.]]></summary></entry><entry><title type="html">VCBF: 2024 Haiku Invitational</title><link href="https://runcode.blog/2024/10/22/vcbf-2024.html" rel="alternate" type="text/html" title="VCBF: 2024 Haiku Invitational" /><published>2024-10-22T05:00:00+00:00</published><updated>2024-10-22T05:00:00+00:00</updated><id>https://runcode.blog/2024/10/22/vcbf-2024</id><content type="html" xml:base="https://runcode.blog/2024/10/22/vcbf-2024.html"><![CDATA[<p>This year’s Haiku Invitational for the Vancouver Cherry Blossom Festival has <a href="https://vcbf.ca/winning_haiku/2024-winning-haiku/">announced its winners</a>.</p>

<p>Unfortunately, I didn’t make the cut this year.</p>

<p>Instead, I share my poems with you:</p>

<h2 id="poem-1">Poem 1</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"Your time will come" they say
as if we couldn't be
cherry blossoms in march
</code></pre></div></div>

<h2 id="poem-2">Poem 2</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Children dying, crying
for mothers. Don't they know
the cherries are blossoming
</code></pre></div></div>]]></content><author><name>Joseph Roque</name></author><category term="poetry" /><category term="writing" /><category term="vcbf" /><summary type="html"><![CDATA[This year’s Haiku Invitational for the Vancouver Cherry Blossom Festival has announced its winners.]]></summary></entry></feed>