8 min read

We built a website and learned everything except coding

Hands raised at a metal festival, silhouettes against warm stage lights
Photo by Roberto Rendon on Unsplash

"Hey dad, can you come pick me up? I don't want to take the bus."

If you've got a teenager, you know the role. Loving taxi driver, on call, no notice.

So there I was on another pickup run with my 17-year-old, the two of us still coming down from four days at Sweden Rock. That's our thing. Once a year we go and watch rock and metal bands together, four days straight. And we were doing what you do on the drive home: arguing about which bands were best, who we'd missed, and of course speculating wildly about next year's lineup. The best show? I couldn't pick between Alestorm and Babymetal. For my teenager, a Venom fan since forever, it wasn't even a question.

At some point I said something like: "You know, you could probably collect the data from every previous year, which bands have played Sweden Rock, cross-reference it against the big bands' tour schedules, and make some educated guesses."

That was it. They were hooked. "That's so cool, can't you write some code and collect the data?"

So this is the story of how we built the Sweden Rock Codex. And how a throwaway line in the car turned into the best hands-on lesson I could have asked for, one that turned out to be about far more than "just coding." Hosting. Caches. Observability. SSH keys. Etc etc.

We've more or less wrapped version 1.0. The prediction part is still coming.

The Sweden Rock Codex landing page: 1,176 different bands since 1992, a stat grid, and a bar chart of bands per year

The coding was the easy part

Here's the thing nobody pictures when you say "I taught my kid to code." We wrote code, sure. An Angular app, some d3 charts, a Python pipeline to wrangle the data. But a lot of that code? The AI wrote it. I sat in the middle as the architect, Claude Code did a lot of the typing, and my teenager watched a working site appear out of a conversation.

If "learning to code" had been the whole lesson, we'd have been done by Sunday.

The coding was the easy part. It always is now, for projects this size.

The interesting stuff, the part I actually got to teach, was everything the AI couldn't decide for us. The "why did we do it this way" questions. And it turns out there's a lot of them hiding inside something as small as "a website that lists some bands."

Where does the data even come from?

The first real question wasn't "which framework." It was "wait, where does this data even come from?"

Sweden Rock's own site actually has a band history. But it's buried behind heavy JavaScript pages, and it's a list to scroll, not something you can slice by subgenre, or country, or how many times a band keeps coming back. We wanted to poke at it from every angle. So we went looking for the raw data instead.

Wikipedia had some of it. setlist.fm had the actual setlists. MusicBrainz filled in gaps. Three free, open sources, sitting there for anyone who bothers to look, none of them complete on its own.

Some of them don't just hand you a file, though. You ask through an API, which meant explaining what an API key even is (a password, basically, that says it's you doing the asking) and why you never paste one into the code where the whole world can read it. Then we met rate limits the hard way. Ask setlist.fm for too much, too fast, and it politely tells you to slow down. So you learn to be a good guest on someone else's server: ask nicely, wait your turn, don't hammer the door.

So we learned to pull from each, line them up, and stitch them into one thing nobody had assembled quite this way before. That's not coding. That's understanding your data: where it comes from, how much to trust it, what to do when two sources disagree.

The Codex broken down by metal sub-genre and country of origin, credited to Wikipedia, setlist.fm and MusicBrainz

We put the master version in a SQLite database, treated it as the single source of truth, and exported the website's data out of it. Why not just type the bands straight into the page? Because data and presentation aren't the same thing, and the day you mix them is the day you start to regret it.

The ideas kept coming

Here's the thing about building something you actually care about: you don't stop at the plan. We set out to list the bands. Then the ideas just kept arriving. What if you could hear them? What if you could see when they're next on tour? What if you could read up on the obscure ones?

And what surprised both of us was how much of it was already a link away, sitting in open data.

So a band page doesn't just sit there. It points outward. Out to Spotify, so you can put the band on right now. Out to Metal Archives for the deep lore. Out to Songkick to check if they're touring near you. Out to the band's own site.

That's the part I think landed hardest. We weren't just pouring data into a box for people to look at and walk away from. The site pulls open data in from all over the web, and then it sends you straight back out into it.

It's not a dead end. It's a junction.

Because the internet was never a pile of separate websites. It's the links between them. And we'd just built one more little knot in the web.

Why we kept history

Then there's git. Not "memorize these commands." The actual question: why would we ever want a complete record of every change we made?

The first time we broke something and went "wait, it worked yesterday," I could show them exactly what changed between yesterday and now. That's when it clicked. History isn't bureaucracy. It's a time machine for when you inevitably make a mess.

The bug loop

Here's my favourite part of the whole project.

My teenager found three real bugs the first night. Not "it looks weird," actual reproducible bugs. One was a band showing up twice, because "Uncle Acid & the Deadbeats" and "Uncle Acid & The Deadbeats" were being counted as two different bands. Lowercase t, uppercase t. Sharp eye.

So they reported them. I fixed them. And each fix got checked before we called it done.

A bug report. A fix. A verification.

Which, honestly, maybe isn't a shock in a house where the dad has been going on about why we test since the kid was far too young to be subjected to it. Some of it soaks in whether they signed up for it or not.

So of course we wrote tests. Not because a textbook said to, but because we'd just felt, first-hand, what it's like when something that worked yesterday quietly stops. That's the only reason tests have ever mattered.

A real server, and what a key actually is

We could have dropped it on some free one-click host. We didn't. I wanted them to see the whole thing, so we put it on a real server I rent (a small Linode), and to log in we used an SSH key instead of a password.

Cue the question: what is a key? Why is this better than a password? And there's a genuinely good lesson hiding in there about something you have versus something you know, and why one of those is a lot harder to steal.

Why the data lives on the edge

Then we bought a domain, pointed it through Cloudflare, and put the site's data files on Cloudflare's edge cache. We did this before we'd told a soul about the site, which is sort of the point.

The little server I rent has all of 2GB of RAM. If every visitor pulled the data straight off it, a busy day could knock it over. So we put the data files on Cloudflare's network instead, copied to machines all over the world, so each visitor gets served from somewhere near them and the box barely notices. We set that up first, on purpose, just in case anyone ever actually showed up.

And then we posted it to Reddit. We didn't break the internet, but a few more people than usual turned up, and the box never noticed them, because Cloudflare was doing the serving instead of my poor little server. We'd braced for a flood and got a steady trickle. That's fine. That's the right order to do it in: you set up for the busy day before it shows up, not while you're watching the thing fall over.

How do you even know it's alive?

Last two. How do you know the site is even up, and how do you know if anyone came?

So we put Netdata on the server (is it healthy, is it about to run out of memory) and GoatCounter on the site (did anyone visit, and from where). And there's a quiet ethics lesson tucked in here too. We picked GoatCounter on purpose, because it doesn't track people. No creepy following anyone around the web, just a count. You can measure something without spying on anybody. That's a choice, and I wanted them to know it's a choice.

The part the model can't do

I wrote a while back about how using AI is more than... well, using AI. About the whole intimidating forest of stuff you actually have to understand before the AI is helping you instead of quietly handing you a mess.

This project was that forest. Except we walked into it on purpose, one tree at a time, because it was fun.

The model wrote a lot of our code. But it cannot care that the box has 2GB of RAM. It can't decide what to do when two data sources disagree, or whether it's okay to follow your visitors around the internet, or why yesterday's working version matters. Someone has to hold all of that. Someone has to be able to explain why it's built the way it's built, and keep it running when it breaks.

That someone is never going to be the AI.

That's the actual job. That's the part I got to teach.

The map lit up

My teenager is, as my friend Fredrik likes to joke, "raised by the internet." It's just there for them, like water, like electricity. You ask it something, it answers.

But there's a difference between using the internet and making a piece of one. Pulling open data out of three different corners of the web, stitching it into something new, putting it on a server you rent, behind a domain you own, and watching it go live for real.

And then the part I'll remember. We put it up, my teenager dropped the link to a few friends on Discord, and that was the entire marketing plan. That same night we sat and watched the visitor map start lighting up. Sweden, obviously. But then the United States. France. Brazil. Singapore. By the next day there was more of it, arriving from who-knows-where, completely on its own. Strangers, on the other side of the planet, looking at the thing the two of us cooked up in the car after a festival, and then actually built.

GoatCounter visitor locations: Sweden, the United States, Norway, France, Brazil, Canada, Germany and Singapore

We told a handful of people. The world showed up anyway.