Posts by John Tornow

February 2, 2024

Lewis Hamilton to Ferrari

Major F1 news came down yesterday: Lewis Hamilton is joining Ferrari next year.

Luke Smith, writing at The Athletic:

It’s the kind of move F1 fans — and the figures at the top of the
sport itself — could have only dreamed of ever happening. Partnering
Hamilton, F1’s most famous and successful driver, with Ferrari, F1’s
most famous and successful team, is box office stuff.
Ferrari will likely enter the 2025 season with the strongest lineup in
F1 as Hamilton races alongside Charles Leclerc, its young star. As
‘superteam’ lineups go, short of the implausible prospect of Hamilton
teaming up with Max Verstappen, it’s hard to think of any bigger.
Regardless of the outcome, this will be one of the defining stories in
F1 for the next couple of years as the 39-year-old Hamilton bids to
write the latest — and potentially final — chapter of his glittering
F1 career in Ferrari’s famous red cars.

This was a complete shock. There’s certainly been rumors for years, but I never expected this to be a reality. How could Mercedes let this happen? Wow.

February 1, 2024

Now Playing: A Web Component

One of the aspects of personal websites that I love is the ability to have a little bit of creative expression and "features" that are just for fun. Case in point here: the ability to see what I'm currently listening to on Spotify. Just for kicks I put this quick component together to do just that. This isn't very complicated at all, and would be simple to set up on your own site if you're so inclined.

Here's what it looks like:

If you're seeing an album cover and song name, it's because I'm listening to music right now at this very moment! Neat. If you see a "Not Playing" label, it's because I'm not listening currently. If now is one of those silent moments, here's an image of what it looks like:

Example of the Now Playing component when a track is playing in Spotify.
Example of the component when a track is playing in Spotify.

The song's name, album, artist, link, and artwork all are populated from Spotify. I'm using the Get Playback State endpoint to get these details. (You'll also need a developer account and an app created on Spotify to do this.)

The Code

Here's the exact code that I used to build the first version of this script. It may change over time, but these are the basics:

Web Component Markup

Inside of your HTML, place the markup for the component itself. I'm using Tailwind CSS for the styling, but you can certainly add your own.

There are templating slots for each of the data points I'm using here (artist, track name, artwork, etc). I'm using slots here to avoid specific class name selectors in the JavaScript below.

In the example above, I'm also using some placeholder markup and a fallback image to display when there's no active track playing. I didn't include that in the code below for simplicity's sake.

<now-playing class="hidden">
  <div class="group max-w-sm" slot="container">
    <a
      href="#"
      slot="link"
      class="border border-gray-200 rounded-lg p-2 flex gap-4 items-center group-hover:bg-gray-200"
      target="_blank"
      rel="noopener noreferrer"
    >
      <span class="max-w-16">
        <img src="" slot="artwork" alt="" class="rounded-lg">
      </span>
      <span class="flex flex-col">
        <span slot="artist" class="text-xs text-gray-500"></span>
        <span slot="track" class="text-sm text-gray-900 font-bold"></span>
        <span slot="album" class="text-xs text-gray-500"></span>
      </span>
    </a>
  </div>
</now-playing>

Custom Element JavaScript

For the JavaScript, I'm using a custom element class to set up the element, fetch the data when it is displayed, and adjust the slots with real data. If I were making this for a more important production website, I'd make this a bit more robust and safe. (Add some code to check to make sure each of the slots exist before I update it, only fetch the data once per page, etc.) But that's not necessary for this site.

class NowPlaying extends HTMLElement {
  async connectedCallback() {
    await this.fetchNowPlaying()
  }

  async fetchNowPlaying () {
    const response = await fetch('https://nowplaying.johntornow.com/')

    // not playing
    if (response.status !== 200) {
      return
    }

    const json = await response.json()

    // not playing
    if (json.isPlaying !== true) {
      return
    }

    this.classList.remove('hidden')

    this.querySelector('[slot="album"]').innerText = json.album
    this.querySelector('[slot="artist"]').innerText = json.artist
    this.querySelector('[slot="track"]').innerText = json.track
    this.querySelector('[slot="artwork"]').src = json.artwork
    this.querySelector('[slot="artwork"]').alt = `${json.artist} - ${json.track}`
    this.querySelector('[slot="link"]').href = json.link
  }
}

customElements.define('now-playing', NowPlaying)

Cloudflare Worker

There's a fetch call in the JavaScript above to a subdomain that I'm using for a Cloudflare worker that handles the server logic. Cloudflare workers are simply amazing, and I love using them for little tools like this. This particular worker is just a file of JavaScript that runs whenever the root of the domain is requested.

In order to get the Spotify bits to work here, make sure to set up a new app on the Spotify Developer portal. Then, ensure you've copied the client id, and client secret into environment variables for the worker.

I also manually created a refresh token for myself using a standard Oauth 2 flow. I did this outside of the app just with Paw, but there's a bunch of other ways to get that value if need be.

A note on scopes: when setting up your initial access token, make sure to use the user-read-playback-state scope so you can read playback information.

There's one other path I took with the worker: some slight caching on the data. I don't know what limits Spotify has on this information, but I didn't like the idea of hitting its servers each page load. So for 3 minutes I'm storing the latest response from Spotify in Cloudflare's Workers KV storage. KV is very handy for this Varnish-style of caching. (I'm also storing my latest access token in KV as well, so each request doesn't need to re-authenticate.)

Since we're loading this worker from a client-side fetch request, I've wrapped all responses with some very loose CORS headers to allow it to work properly in the browser.

import { Buffer } from 'node:buffer'
import get from 'lodash.get'

// Get current access token from Spotify, using refresh token if need be
const fetchAccessToken = async (env) => {
  const clientId = env.CLIENT_ID
  const clientSecret = env.CLIENT_SECRET
  const originalToken = env.REFRESH_TOKEN

  const savedAccessToken = await env.spotify.get("access_token")

  if (savedAccessToken) {
    return savedAccessToken
  }

  const response = await fetch('https://accounts.spotify.com/api/token', {
    method: 'POST',
    headers: {
      'content-type': 'application/x-www-form-urlencoded',
      'Authorization': 'Basic ' + (new Buffer.from(clientId + ':' + clientSecret).toString('base64'))
    },

    body: new URLSearchParams({
      grant_type: 'refresh_token',
      refresh_token: originalToken,
      client_id: clientId
    })
  })

  if (response.status !== 200) {
    return null
  }

  const json = await response.json()
  const newAccessToken = json.access_token

  // store the new token in KV store for an hour
  await env.spotify.put("access_token", newAccessToken, { expirationTtl: json.expires_in })

  return newAccessToken
}

const fetchSpotifyData = async (accessToken) => {
  console.debug('fetching current track from Spotify...')

  const response = await fetch('https://api.spotify.com/v1/me/player', {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${accessToken}`
    }
  })

  // only 200 OK status will continue
  if (response.status !== 200) {
    return null
  }

  const json = await response.json()

  // for debugging spotify response
  // console.log('spotify data:', json)

  const result = {
    isPlaying: false,
  }

  try {
    result.artist = get(json, 'item.artists[0].name')
    result.album = get(json, 'item.album.name')
    result.track = get(json, 'item.name')
    result.artwork = get(json, 'item.album.images[0].url')
    result.link = get(json, 'item.external_urls.spotify')
    result.isPlaying = json.is_playing
  } catch (e) {
    console.error('error parsing spotify data:', e)
  }

  return result
}

// to allow this to be loaded from my site (and any other really)
const responseWithCors = response => {
  response.headers.set('Access-Control-Allow-Origin', '*')
  response.headers.set('Access-Control-Allow-Methods', 'GET,OPTIONS,HEAD')
  response.headers.set('Access-Control-Max-Age', '86400')

  return response
}

export default {
  async fetch(request, env, ctx) {
    // check for a cached now playing track in KV
    const cachedCurrentTrack = await env.spotify.get("current_track")

    if (cachedCurrentTrack) {
      console.debug('Current track found in cache')

      const cachedData = JSON.parse(cachedCurrentTrack)
      return responseWithCors(Response.json(cachedData))
    }

    const accessToken = await fetchAccessToken(env)

    // if no token, we'll just skip the rest and assume nothing is playing
    if (!accessToken) {
      return responseWithCors(new Response(null, { status: 204 }))
    }

    const spotifyData = await fetchSpotifyData(accessToken)

    // spotify returns 204 no content if nothing playing, so we'll do the same
    if (spotifyData === null) {
      return responseWithCors(new Response(null, { status: 204 }))
    }

      // store the current track in KV for a few minutes for caching
    await env.spotify.put("current_track", JSON.stringify(spotifyData), { expirationTtl: (60 * 3) })

    return responseWithCors(Response.json(spotifyData))
  }
}

That's about it. There's some nuance to figure out with the Cloudflare workers for sure. But the basics are right here.

If you found this useful, let me know!

This concept was inspired by Cory Dransfeldt’s post about the same thing. Thanks Cory for the cool idea.

February 1, 2024

The Orioles are being sold

John Ourand, writing for my friends at Puck:

It looks like the Orioles sale is finally going to happen. I’ve had several plugged-in sources tell me that the team’s owner, John Angelos, has agreed to sell the franchise to a group led by two private equity billionaires: David Rubenstein, who started the Carlyle Group, hails from Baltimore, and has been tied to the deal for months; and Ares Management Corp. co-founder Mike Arougheti, who lives in New York. The extent of Arougheti’s involvement is unclear, but Rubenstein will become the “control person,” the term MLB uses for teams’ decision-makers. The deal values the club at $1.725 billion.

I never thought I’d see this day come. Is it a coincidence that it happened after the club’s most successful year since the early ‘80s? Probably not. Call me optimistic. This sounds great. (Can’t get any worse, to be honest.)

February 1, 2024

Adjusting Micro.blog

I wasn’t happy with how the cross-posting was going on Micro.blog. Especially for just external link posts, they looked a bit funny. I’ve added a secondary feed now that just posts these small status updates and any original posts. Links will continue to be on the main blog here. Steadily improving things as I go around here…

January 30, 2024

Arc Search

Arc Search is a new delightful and incredibly useful app from the Arc team. I keep trying to get used to Arc on my Mac but it hasn’t stuck for me. I just love the simplicity of Safari and it’s hard to shake for everyday browsing.

The mobile app is something entirely different. It’s a full browser to replace your default, yes, but it’s really focused on searching first and foremost. I love being able to type a search phrase and have the app “browse for me.” The “browse for me” feature summarizes the first few search results and makes a lovely compiled webpage for you to answer a question and give you links to sources.

Comparing this with a modern Google search result page is a breath of fresh air. No scrolling past 10 ads to get to an original content source. This is the best use of AI I’ve found for summarizing web search yet. It’s so cool and nicely designed. I don’t know if it’ll replace Safari on the phone for general browsing and reading, but I will definitely be using this for searching and research.

January 27, 2024

Week Notes: January 27, 2024

Happy Saturday. A few notes from the week about work and life…

We’re working on an integration with SMS notifications through Twilio. Most of its platform is really quite nice and a pleasure to work with. Switching between SMS and Whatsapp was incredibly easy.

Getting started with a new company on Twilio: not so easy though. I understand the need to prevent spam text messages and such, but it shouldn’t be so difficult to set up an account and get verified. I’ve had a relationship with this company for over a decade, and I use one of its acquired companies (SendGrid) to send over a million emails a week. A few dozen text messages shouldn’t be a problem!

If it’s so hard to get set up sending SMS messages, why do I still receive so many unsolicited political messages!?

Stripe continues to be the gold standard for third-party APIs. I wish every company would treat its developer community like Stripe. Incredible documentation. Thorough examples. Clear messages when things go wrong. Versioning and change notes that were written by humans. Lovely all around.

The third-party integration we dread updating? Shopify. The opposite of Stripe in every way. We’re forced every few months to update our API version through increasingly hostile means. The documentation is inconsistent, incomplete, and often just plain incorrect. No specific notes on what features are deprecated within our requests, just generic notes that what we’re doing is incorrect. It took a few folks on our team almost a week to upgrade our very basic usage of Shopify’s APIs. Not great! And, we’ll be forced to do it all over again in a few months.

Another great integration and provider of ours: Cloudflare. Incredible how much value we get from so little expense. One of our worker processes on Cloudflare served 24,697,912 requests this month. The cost? $3.60. Incredible.

Software timelines are nearly impossible to predict when creating something from nothing. This is a struggle on one of my small teams. We’re all working towards a goal, but it’s always a challenge to predict when things will be done. I’ve not found a tool or methodology that can help with this, and I’ve tried them all. Sometimes things are just going to take how long they’re going to take. And that’s okay.

Coffee with a friend this week was incredibly helpful for motivation and support. Starting, building, and running multiple software companies is a tough business. Some fresh perspective from a person that “gets it” meant the world.

And a few links for the week:

Spyglass – M.G. Siegler’s new site. Nice to see M.G. writing and putting out new content, I’ve always liked his work and opinions.

LM Studio – I’ve been messing with AI models quite a bit lately. It’s such an incredible new set of tools and technologies that’s changing our world. LM Studio is a super handy application that simplifies downloading and running various models locally on my Mac.

I know I’m biased here, but today’s issue of Air Mail is really great. The quality of work this team (not me, the editorial team!) is producing on a weekly basis is incredible and I’m so proud of this group. In this issue: my buddy Nathan King got an early look at the Vision Pro (jealous!) and Buzz Bissinger, author of Friday Night Lights, mourns the loss of Sports Illustrated.

Tomorrow afternoon: AFC Championship between the Chiefs and my beloved Ravens. Cautiously optimistic today. Really excited for this one. If you want to be the champs, you have to go through KC. Let’s see if Lamar and the boys can get it done.

And finally, March can’t come soon enough for me with the return of F1. This week we saw contract extensions for Charles Leclerc and Lando Norris. Glad to see the stability. Oh, and we have a new name for AlphaTauri: The Visa Cash App RB team. Just rolls off the tongue.

✌️❤️

January 25, 2024

The Mac at 40

Yesterday was the 40th anniversary of the introduction of the Macintosh computer. I can’t think of any device that has impacted my life more, even including the iPhone. In some hypothetical dystopian scenario where I could only keep one device, it would be my Mac. I switched to the Mac as soon as I could afford to do so and have been using it for over half of its 40 year history.

Happy Birthday, Mac. Many happy returns.

January 22, 2024

Sports Illustrated

Sad news last week reported by A.J. Perez at Front Office Sports:

Staffers at Sports Illustrated were notified on Friday of massive layoffs—some immediately, others in short time, with potential for the entire staff to be gone in three months.
Authentic, the licensing group that purchased Sports Illustrated for $110 million from Meredith five years ago, has terminated the agreement it holds with The Arena Group to publish SI in print and digital, according to an email obtained by Front Office Sports. That move comes three weeks after Arena missed a $3.75 million payment that breached the company’s SI licensing deal, which began in 2019. (Authentic’s notice of termination, meanwhile, triggered a $45 million fee due immediately to Authentic, according to an SEC filing on Friday.)
The fallout: On Friday Arena told SI employees in an email “… We were notified by Authentic Brands Group (ABG) that the license under which the Arena Group operates the Sports Illustrated (SI) brand and SI related properties has been officially revoked by ABG. As a result of this license revocation, we will be laying off staff that work on the SI brand.”

I enjoyed Peter King’s callout in his column this week about SI:

Nothing describes how the sports media business has changed better than the precipitous decline of Sports Illustrated. More than a bit of melancholy washed over me Friday, processing the news of the battered place. Because even if SI survives 2024, it will do so as a skeleton of what it was.
I have only good memories of my 29 years with the franchise. In the midst of the sadness and bitterness over SI’s demise, I want to share a few of the reasons why I will always consider myself the luckiest man on the face of the earth because I got to work for the greatest sports journalism franchise for the guts of my career.
I remember the phone call—absolutely, totally out of the blue—from managing editor Mark Mulvoy in spring 1989. I was 31, covering the Giants for Newsday. Mulvoy asked if I was interested in interviewing for a job at the magazine. It’s still one of those things to this day that I can’t quite believe happened. I went into the mag’s Rockefeller Center offices, across from Radio City, and Mulvoy got to the point pretty fast. He wanted me to write the “Inside the NFL” column and, in fact, there wasn’t much of an interview. He asked me if I wanted the job.

He goes on to ask a bunch of people about their favorite covers and SI memories. Really cool.

January 22, 2024

Notion Calendar

A new calendar app from Notion was announced this month. Notion’s calendar looks very nice. It appears to be (like Notion itself) a web-based calendar app but with a few native app wrappers. The integrations are nice: I was able to easily sign-in with my various Google Calendar accounts and also to a few Notion workspaces.

Overall it is a very nice little calendar app. Also nice that it’s just built into our existing paid Notion accounts so there’s nothing else to buy.

Design-wise it’s very clean and clear. One of the reasons I’m attracted to Notion is its clean design. The calendar fits right in.

The ability to connect to a Notion database in a calendar is really super nice and it’s something I really have wanted from Notion itself. I wish I didn’t need to use their calendar app to get my data out into a calendar. I’d much prefer to use Fantastical, but it doesn’t seem to be possible without third-party apps just yet.

It’s hard not to compare this launch with that of the HEY Calendar launched a few weeks earlier. Notion is a much more traditional approach to a calendar app. Both were easy to integrate with for external calendars. HEY’s approach is very different and thoughtful. Trying them both out for a few weeks to see which ideas stick.

January 22, 2024

NFL Divisional Round

Great weekend of football this weekend. The divisional round does not disappoint. The Ravens game was a bit stressful to begin the game, but they really turned it on during the second half. Good to see Lamar get a solid playoff win to silence his critics (for a few days, at least).

Sad to see the Bills lose last night. They played so well, but if you want to be the champs you’re going to have to beat the Chiefs sooner or later. Excited to see Jason Kelce at one more game this year.

Next weekend should be a lot of fun. I’m feeling optimistic about the Ravens taking care of business. Opening line is BAL -3. Basically a tie with the slight advantage going to home field advantage. I like it.

January 20, 2024

Cheerful Electronic Music

Very nicely done video “guided tour” of the Vision Pro. This is Apple at its best, not fighting with its developer community.

January 19, 2024

Friday Links: January 19, 2024

Happy Friday. A few links and thoughts from the week:

The Obsessor – Matthew Panzarino’s new site launched late last year after he left TechCrunch. Added a $6/mo paid tier this month. Powered by Ghost, which seems to be a popular non-Substack solution these days.

reMarkable – reMarkable e-ink tablet for writing. I really like the idea of this. Pricing is reasonable, but with a pen and case adds up quick. I’ve been using an iPad for this purpose for now, but this is a really interesting idea too.  (via The Obsessor)

Justin Jackson’s newsletter focus this year is on marketing and growth tips for SaaS founders. Subscribed.

Prompt 3 – Third version of the great terminal/SSH app for iOS and now the Mac. Been using the previous versions on my iPad for years. (Also, Panic’s app pages are just the best.)

Bluesky 2023 Moderation Report – Interesting to me to see a social platform reporting this type of data. Maybe it’s been done by others, but this is the simplest and clearest I’ve seen. There’s a lot of tooling behind the scenes to make this work.

Iowa sues TikTok over mature and inappropriate content accessible to minors. Don’t let your kids use TikTok people. Or any social media if you can.

Amazon to invest in Diamond Sports and bail out the bankrupt and poorly run regional sports networks. Three cheers for Amazon here. Please let us stream local sports easily.

Last but not least: pre-orders opened up this morning for Apple’s first Vision Pro release. Very curious how this release is going to go.

January 18, 2024

Apple and App Payments on the Web

Apple updated its developer guidelines this week to “allow” for external linking to third-party payment sources for app developers. From the Apple Developer site:

In addition to using Apple’s convenient, safe, and secure in-app purchase system, apps on the App Store in the United States that offer in-app purchases can also use the StoreKit External Purchase Link Entitlement (US) to include a link to the developer’s website that informs users of other ways to purchase digital goods or services.

The change comes with many specific requirements, including:

The link you provide in your app must:
  • Go directly to your website without any redirect or intermediate links or landing page;
  • Open a new window in the default browser on the device, and may not open a web view;
  • Not pass additional parameters in the URL in order to protect the user (for example, their privacy);
[…]
  • May not be displayed on any page that is part of an in-app flow to merchandise or initiate a purchase using in-app purchase.

I understand not wanting any tracking or privacy tracking parameters in these links, but it’s not a great user experience to be dumped out on a generic web page, requiring you to either sign-in or find what you were trying to purchase manually.

Apple is using a 7-day attribution window for all purchases made on the web through these links, and a commission of 27% (or 12% if you’re in the Small Business Program):

Apple is charging a commission on digital purchases initiated within seven days from link out, as described below. This will not capture all transactions that Apple has facilitated through the App Store, but is a reasonable means to account for the substantial value Apple provides developers, including in facilitating linked transactions.
[…]
If you adopt this entitlement, you will be required to provide transaction reports within 15 calendar days following the end of each calendar month. Even if there were no transactions, you’re required to provide a report stating that is the case.

There’s just a ton of hoops to jump through and extra work to make this happen, and developers still will owe the 27% commission for the sale externally. What’s the point? I don’t see how any developer actually goes through the effort of doing this, and that’s the point.


This is quite the change and has caused a justified uproar in the Apple development community:

Tim Sweeney, CEO of Epic Games, on Twitter/X:

Apple filed a bad-faith “compliance” plan for the District Court’s injunction. It totally undermines the  order allowing “buttons, external links, or other calls to action that direct customers to purchasing mechanisms, in addition to IAP”.
[…]
Epic will contest Apple’s bad-faith compliance plan in District Court.

Brent Simmons:

Apple doesn’t care about you personally in the least tiny bit, and if you were in their way somehow, they would do whatever their might — effectively infinite compared to your own — enables them to deal with you.
Luckily, Apple has just provided us all with a reminder. Just like the sixth finger in an AI-rendered hand, Apple’s policies for Distributing apps in the U.S. that provide an external purchase link are startlingly graceless and a jarring, but not surprising, reminder that Apple is not a real person and not worthy of your love.

Graceless and jarring indeed.

John Gruber, on Daring Fireball:

Apple should have been looking for ways to lessen regulatory and legislative pressure over the past few years, and in today’s climate that’s more true than ever. But instead, their stance has seemingly been “Bring it on.” Confrontational, not conciliatory, conceding not an inch. Rather than take a sure win with most_ of what they could want, Apple is seemingly hell-bent on trying to keep everything.

Nick Heer, on Pixel Envy:

Developers sure will have a lot of paperwork to complete in the near future if they want to take advantage of these additional capabilities. Apple is creating this bureaucracy because it says this is how it gets paid to develop iOS; Judge Yvonne Gonzalez Rogers found, on page 114 of her decision (PDF) Apple’s arguments were “pretextual, but not to the exclusion of some measure of compensation”. I find that line questionable mainly because Apple has developed MacOS continuously for over twenty years without taking a commission on digital purchases. But who am I to question the U.S. legal system?

Ben Thompson, in a Stratechery Daily Update:

I don’t, to be clear, like this state of affairs. While an iPhone may not be a technical standard, I do think that it is an essential platform for businesses of all kinds, and that Apple reaps a multitude of benefits from having a thriving app ecosystem. At the end of the day, though, Apple’s intellectual property is their property, and they get to charge whatever fee they like. That they will jump through whatever hoops are necessary to collect said fee should not be a surprise to anyone at this point.

Manton Reece:

I like Tim Cook, but there are moral issues he seems completely blind to, like this 27% tax nonsense. Forget iOS. By Apple’s logic, they could also charge 27% (or anything!) for any business that has a Mac app and links to their web site. Never in computing have we seen a company so overreach.

David Heinemeier Hansson, never one to mince words:

Apple would be wise to study the long arc of Microsoft’s history. Learn that you can win the battle, say, against Epic, and end up losing the war for the hearts and minds of developers. And that while the price for that loss lags beyond the current platform, it’ll eventually come due, and they’ll rue the day they chose this wretched path.

I don’t have any issue with Apple expecting some sort of payment or commission on purchases in the App Store, and a reasonable attribution of other purchases. It’s their store, their platform, and their prerogative to do so. The 27% isn’t a payment processing fee, it’s an “IP license”, which Apple is going to collect from developers no matter what. I think this license is extremely high and aggressive, but they can charge what they decide.

There’s a reason I’ve spent most of my career working on the open web. Sure, the web has its deep flaws, but I can’t imagine running a sustainable long-term business subject to the whims and demands of one corporation. In the early days of the app stores, this type of behavior would have been more reasonable (I guess), but mobile devices are (as Ben notes above) an “essential platform for businesses of all kinds” and it’s a shame that there are so many rules and regulations governing how they work.

The past decade or so of the Tim Cook era at Apple has been mostly positive: incredible shareholder value generated, industry-leading environmental policies, and of course some amazing products. But I think the long-term stain on Apple and this era will be its relationship with China and how it treats the App Store especially its entitlement to receive a “license fee” for all commerce on its platforms.

January 16, 2024

A Plea for Lean Software

Niklaus Wirth’s writing in 1995 is just as relevant today as it was then. Some gold in here:

The belief that complex systems require armies of designers and programmers is wrong. A system that is not understood in its entirety, or at least to significant degree of detail by a single individual, should probably not be built.

Communication problems grow as the size of the design team grows. Whether they are obvious or not, when communication problems predominate, the team and the project are both in deep trouble.

Reducing complexity and size must be the goal in every step—in system specification, design, and in detailed programming. A programmer’s competence should be judged by the ability to find simple solutions, certainly not by productivity measured in “number of lines ejected per day.” Prolific programmers contribute to certain disaster.

Some of the best programmers I’ve ever worked with wrote the least amount of code.

To gain experience, there is no substitute for one’s own programming effort. Organizing a team into man- agers, designers, programmers, analysts, and users is detrimental. All should participate (with differing degrees of emphasis) in all aspects of development.

Programs should be written and polished until they acquire publication quality. It is infinitely more demanding to design a publishable program than one that “runs.” Programs should be written for human. readers as well as for computers. If this notion contradicts certain vested interests in the commercial world, it should at least find no resistance in academia.

I was meeting with an advisor this week and one of his pieces of advice was very similar to this last point: We often get only one shot at a first impression in software, especially for B2B hosted software. Make sure it’s ready and polished up. If we need more time, then take it to build, but don’t rush something out. An “MVP” isn’t a good thing in the B2B space.

(via Daring Fireball)

January 15, 2024

Snow Day

Snow on leaves of a magnolia tree

First snow of the season here in Dallas. Not much of a snow storm, but still a nice dusting of the area. A few dry flakes collected on a magnolia tree.