The AI Was 5% of the Work

Hand-drawn infographic: 104 World Cup games flow through The AI Engine into a personalized watch list with email, calendar, and share options

You have been told that AI is the thing that separates people who can build from people who cannot. It is not. I know, because I built a real AI app over a weekend, and calling the AI was the part I spent the least time on by far.

The hard part was everything else. The thinking before the code, the plumbing underneath it, the dozens of small decisions nobody ever sees. That is the 95% nobody talks about, and it is the part that actually decides whether what you build is any good.

Here is what I built: a tool that looks at all 104 games of the 2026 World Cup, asks which teams you care about and how much time you have, and hands back the exact list of games worth your while. It is live. You can use it right now at singla.ai/worldcup.

The Idea Took Five Minutes

The World Cup is 104 games in 39 days. No one has time for that.

I care about a handful of teams. I have a job, two kids, and roughly the same number of free evenings as anyone else. I was never going to watch all 104, and I did not want a generic “best games to watch” list written for everyone, because I am not everyone. I wanted the games that mattered to me. My teams, and the games that count, the ones that decide who advances and who goes home.

That is the whole idea, and there is nothing clever about it. Good ideas rarely are. They are just a specific version of a problem a lot of people quietly have. But identifying the problem is the easy part. Figuring out how to solve it well is the real work.

I will be honest about the second reason this existed: I wanted to put a new AI engine to work on something real. There is no better way to learn a tool than to build an actual thing with it, start to finish, and a personal project is the safest place to do that. If it broke, it broke on a soccer app, not on a client’s business. Testing new tools on something low-stakes first is most of what separates a calm build from a painful one.

I Sketched It on a Napkin

Before I wrote a single line of code, I sketched the whole thing out.

Not the code. The experience. I walked through what a person actually does, step by step. They pick their team. They add a couple of other teams they follow. They set how many games fit their life. They tap a button and wait a few seconds while something thinks. They get a ranked list. Then they do something with it. Save it, email it, drop it in their calendar, share it.

That is the entire app, and I had all of it mapped before I picked a single tool. That order matters. The sketch is the spec. Every technical decision that came later, the database question, the storage question, the AI question, got answered by pointing back at the sketch and asking what does this step actually need. When you design the experience first, the technology stops being a series of open questions and becomes a series of answers to questions you have already asked.

Most people do this backwards. They pick the shiniest new tool and then figure out what to build with it. Then they are surprised when what they built feels like a pile of code instead of a product people actually want to use. Five minutes on a napkin will save you a week spent building in the wrong direction.

Hand-drawn napkin sketch on a cafe table showing the World Cup watch list app flow: pick your team, add teams you follow, set how many games fit your life, hit go, AI ranks the list, then save, email, or share

The Newest Tool Is Usually the Wrong One

Then came the question of what to build it on. The language, the framework, the infrastructure underneath it. I reached for what I already had running.

There is a strong tendency, on a project like this, to treat it as an excuse to learn the newest framework everyone is posting about. I did not. I built it on the same infrastructure my site already runs on. Tools I had used before, whose limitations I already understood, that were battle-tested and stable. Nothing new to install, nothing new to learn on a deadline, no surprises lurking in the corner.

The goal of this project was to test an AI engine, not to test a new web framework at the same time. Every new tool you add to a build is one more thing that can break in a way you do not understand yet. By keeping everything except the AI familiar, I made sure that when something went wrong, and things did go wrong, I would know right away whether the problem was the new piece or one of the old ones. It was almost always the new piece.

Reach for what you already know, and spend your energy on the one new thing worth learning. For me, it was the AI engine.

The Graphics Took 17 Tries

A good app is better with good graphics. This is the part most technical write-ups skip, and it ate more time than you would think.

The header image alone took seventeen tries. I was using an image generator from Google called Nano Banana, and working with it was nothing like placing an order. You ask for a plain hamburger and get a cheeseburger, with lettuce, tomato, onion, and pickles. Every request came back with three extra enhancements you never asked for. I would ask for a warmer wall color and it would quietly move a lamp. I would ask to shift one element and it would redraw a face. Every round was as much about catching what it changed behind my back as it was about getting the change I wanted.

Buried inside those seventeen rounds were real choices. The color of the walls. The jerseys on the people. Where the television hung. How much warmth the room had, and where the focus landed.

And then there was the shape itself. This header was not a standard ratio like 4:3 or 16:9. It was a wide, thin banner, and getting it to look right on a desktop and a phone at the same time, with nothing important cropped off, was a struggle every single time.

Here is the takeaway. Working with a generative tool is a negotiation, not a command. You are not instructing it, you are learning how it thinks and shaping your feedback into something it understands. That is the whole skill.

Timeline of hero image iterations from early concept sketches through draft, polish, UI mockup, to the final World Cup watch list illustration

England Is Not a Country

Every team gets a little flag next to its name. That seems simple enough.

Every country has a standard two-letter code, the ISO country code used by software everywhere. France is fr. Brazil is br. Japan is jp. Map the code to the flag and you are done. You would assume every team works that way.

England is not a country. At least not to the ISO standard, which treats it as part of the United Kingdom under the code gb. Ask for that and you get the Union Jack, not the red and white St George’s Cross England actually plays under. England needs a special subdivision code, gb-eng. Scotland, also in this tournament and also part of the UK, needs gb-sct for the Saltire. Three different flags hiding behind one country code.

Miss this, and English fans would riot. They load the page, see the Union Jack on their team, and lose all faith in the rest of the tool. Nobody praises you for getting the flag right. They just quietly trust everything else, because the one thing they checked was correct. Get it wrong and the opposite happens. You only catch something like this if you know to look for it. That is what experience buys you.

Illustration of three knights on hobby horses with St George's Cross, Saltire, and Union Jack flags, labeled GB-ENG, GB-SCT, and GB wrong for England, with a fan saying That's not our flag

I Threw Out the Calendar Library

The app shows every match on a calendar and highlights your picks. There are some great free, open-source calendar libraries out there, like FullCalendar and TOAST UI Calendar, and they look fantastic. I tried them.

They were wrong for this app. Too heavy, built to do a hundred things when I needed them to do one. I was spending more time fighting the library than I would spend building the small piece I actually needed.

So I built my own instead. A simple, scrollable list of matches grouped by day, mobile-friendly from the start, with the AI’s picks highlighted. That is all it needed to be.

A library is a good deal when it does roughly what you want. It is a bad deal the moment you spend your days bending it against its will. A small custom component that does one thing, exactly the way you want it, almost always beats a big library you are fighting. Knowing which situation you are in is the kind of judgment that comes with experience. If you are just starting out, that instinct is worth building early.

The Data Lied to Me

I pulled the match schedule from a public feed. And the feed lied to me.

One team arrived with the country code IR. That part is correct, it is Iran. But the name attached to it said “Iraq.” Two different countries, one confidently wrong. If I had trusted the feed, I would have shown Iraq’s name next to the Iranian flag, and made it look like I had no idea what I was doing.

That was not the only one. The feed called South Korea “Korea Republic” and Czechia “Czech Republic.” None of these are wrong, exactly. They are just not the friendly names a user expects. Someone looking for their team scans for “South Korea,” not “Korea Republic.” The feed had the technically correct label. I needed the one people actually recognize. So I built a post-processor. A small layer that takes the raw feed and corrects every one of these before the data touches the app.

'Korea Republic' => 'South Korea'
'Czechia'        => 'Czech Republic'
'Iraq'           => 'Iran'

Then there was the bracket itself. Two-thirds of the way through a World Cup, half the future matches do not have teams yet. They have placeholders like “winner of Group A” because the real teams have not been decided. The feed represents those as cryptic codes, and the app has to know the difference between “a real team is playing here” and “this slot is still being decided.” That distinction turned out to matter enormously once the AI got involved, as you will see.

The lesson is an old one: trust but verify. Real-world data shows up messy, inconsistent, and sometimes just wrong. The slow, unrewarding work of cleaning it is the foundation everything else stands on. Skip it and nothing downstream can save you.

Why I Didn’t Use a Database

It sounds reckless. The app has no database of users. None. No sign up, no login, no accounts.

Nothing about you is stored on the servers. When you select your favorite team and set your preferences, that information is saved in a browser cookie, on your device. Come back next week and your watch list is waiting, because your browser remembered, not because I am tracking you.

A database would have stored your preferences just fine. A cookie stores them too, and it keeps everything on your device, so it does the job and protects your privacy at the same time. There is no database to hack. Storing nothing about you is not a compromise. It is a promise, and it is the truth.

The schedule needs to live somewhere. In this case it is a small JSON file, identical for everyone who visits. The app holds onto two small things while it runs: a count of how many lists have been built, and a limit that stops anyone from overloading the tool. Neither is tied to a person, and both live in a small in-memory store. Developers reach for a database by default. It is worth asking instead what actually needs to be stored, and for how long. The answer is usually less than you think.

Sending Email Is Harder Than It Looks

After your list is generated, you can email it to yourself. The email includes the games, the kickoff times adjusted for your timezone, and an ICS file, the standard calendar format that lets you add events directly to your own calendar with a single click.

It feels like a simple feature. It is not.

Here is what nobody tells you. Sending email that actually arrives is hard. Your server cannot just send a message and expect it to land. Modern mail systems distrust everything by default. You need a proper mail relay service, and you need your domain set up to vouch for it: a small stack of DNS records, things like SPF and DKIM, that prove the message is really from you and not someone forging your name.

For this I used ZeptoMail, Zoho’s transactional email service. A transactional system sends individual, customized messages one at a time, triggered by an event, as opposed to blasting one newsletter to a giant mailing list. Different tool, different job.

The takeaway: this feature took an afternoon instead of days, only because I had used ZeptoMail before and the domain setup was already done. It pays to build infrastructure you can reuse. The second time you reach for it, the hard part is already behind you.

Hand-drawn infographic: web server emails blocked by spam and distrust until they pass SPF, DKIM, and DMARC shields through a ZeptoMail transactional relay bridge into the inbox with ICS calendar attachment

The AI Goes in the Middle, Not the End

Most people picture AI as the last step. You gather your data, you do your work, and at the very end you hand it to the AI to produce the answer. For this app, that is backwards.

The AI sits in the middle. There is real work before it and real work after it.

Before the AI runs, the app does the precise, factual work that AI is bad at. It reads the current standings. It works out which teams are still alive and which are already out. It separates the matches with real teams from the ones that are still empty bracket slots. Then it packages all of that into a clean, structured summary. The AI never touches the raw, messy feed. It sees an organized briefing of exactly where the tournament stands right now.

Then the AI does the one thing it is genuinely good at: judgment. Given this fan, these teams, this much free time, and this state of the tournament, which games are worth watching, and in what order? It hands back a ranked answer.

After the AI runs, the app takes over again. It attaches the real kickoff times, the venues, the flags, the calendar details. The AI supplies the opinion. The code supplies the facts.

That separation is the most important decision in the whole project. Facts before, judgment in the middle, facts after. It is why the results hold up. You never ask the AI to remember a kickoff time or spell a country correctly. You ask it for an opinion, and you wrap that opinion in facts you control.

Why I Didn’t Use Claude or ChatGPT

This is the question I get most often. So here is the honest answer.

I did not use the two names everyone knows. I used Grok, the model from Elon Musk’s company xAI. Three reasons, and the reasoning matters more than the choice.

First, this was a deliberate test. I wanted real, hands-on time with an engine I had not built anything serious with yet. A small, contained project is the right place to learn a new tool, where the stakes are low and a failure costs me nothing.

Second, the tournament is happening right now, and Grok has live access to current events through its connection to X. For a project built entirely around what is happening this week, that mattered.

Third, cost. The World Cup app is free, and anyone can run it as many times as they like. That makes the price per request matter enormously. It is the difference between a project I can leave running and one I have to shut down. Grok is dramatically cheaper than the alternatives, and for this kind of work the gap is not close.

Here is what the same work costs across the three flagship models, priced per million tokens:

Model Input Output
GPT-5.5 (OpenAI) $5.00 $30.00
Claude Opus 4.8 (Anthropic) $5.00 $25.00
Grok 4.3 (xAI) $1.25 $2.50

Prices per million tokens, as of June 2026.

Output is where the real money goes, because that is the text the model actually generates. Grok runs at $2.50 against $25 and $30 for the others. That is roughly ten times cheaper for the part of the job that costs the most.

None of this means Grok is the best model for everything. It is not. The point is smaller and more useful than that. I did not default to the famous name. I looked at what this specific project actually needed, current data and a cost low enough to keep the World Cup app free for everyone, and I picked the tool that fit. Then I built it so the choice is never locked in, because requirements change, and the right answer next month may not be the right answer today.

How to Train an AI

I asked the model a clear question and got back a clear, reasonable, completely useless answer.

Reasonable and useless is the trap. The first results were not wrong. They were generic. The model recommended the obvious marquee games anyone could have named without a tool. The entire point of this app was personal, current recommendations, and the raw model delivered neither. Getting from “it works” to “it is actually useful” took dozens of specific instructions, each one added because something went wrong without it. That work is the training. Here is what it looked like.

  • The counting bug. When a fan says “I have time for eight games,” they mean eight games, total, for the rest of the tournament. The model kept reading that as eight games per week and multiplying it across the remaining schedule, handing back far too many matches. I had to tell it plainly: this is a total, not a weekly number, and you must not multiply it by anything.
  • The undecided-game problem. Remember the empty bracket slots, the matches where the teams are not decided yet? Left alone, the model recommended them constantly, because later-round games sound important. But nobody can get excited about a match when the teams are unknown. So I capped how many of those it could suggest, and forced it to be honest when it did: to say plainly that this one is a forecast and the teams could still change. The one exception I wrote in by hand is the Final. It always makes your list, even before we know who is in it, because it is the Final.
  • The eliminated-team problem. If your favorite team has been knocked out, you do not want to keep being told to watch their games. So the app feeds the model the list of eliminated teams, and tells it: if their team is gone, say so directly, then find them the best games left.
  • The rambling problem. Left alone, the model wrote paragraphs, and long answers risk getting cut off halfway through. So the instructions cap nearly everything. The intro stays short. Each game gets a brief note, not an essay. And it has to answer in a strict, structured format the app can read every time, clean data, not loose prose.
  • The safety net. If the model fails completely, too slow, garbled, anything, the app does not show an error. It quietly falls back to a sensible list built from the same data. The AI is the star, but the show goes on without it.

There is a tidy phrase for all of this, prompt engineering, that makes it sound cleaner than it is. It is really just careful instruction. Every rule above exists because the model misbehaved in a specific way until I wrote the rule that stopped it. The model is the engine. The training is the steering, the brakes, and the guardrails. The afternoon I spent calling the AI was easy. The days I spent teaching it to be useful were the real work.

I have not pasted the full prompt here. It is long and hard-won, and I would rather not put it out in the open. But I am happy to send it to anyone who wants it. Message me on LinkedIn and it is yours.

Hand-drawn workshop infographic: AI engine on a lift with steering, brakes, and guardrails labels, sticky notes for counting bug, undecided games, eliminated teams, no rambling, and safety net, plus before and after watch list panels

Your List Goes With You

A watch list is only useful if you can take it with you. So once the AI hands back your picks, there are three ways to get them out of the app, and I built all three on purpose, because different people do different things with a list.

  1. Add it to your calendar. The email includes that ICS file we talked about earlier, the standard calendar format, so every game drops directly into Google, Apple, or Outlook, at the right time in your timezone, without you typing in a thing.
  2. Email it to yourself. The email has everything: your games, the kickoff times in your timezone, all of it in your inbox.
  3. Download it as an image. A clean, shareable graphic of your games. I built this by designing one background template, then having the code render your specific picks on top of it. The template does the visual work. The code just fills in the names, times, and flags. Every list comes out looking custom built, without me having to design each one.

Three formats, because there are three kinds of people. The one who lives in their calendar. The one who lives in their inbox. And the one who wants to share their picks, so they take the image. Meeting people where they already are, instead of forcing them into the one path you happened to build, is a small amount of extra work. It is almost always worth it.

The Real Takeaway

Back to the title. If the AI was only 5% of the work, where was it?

Right in the middle. One focused step that takes a clean briefing and returns a smart, ranked opinion. That step is real, and it is valuable. It is the thing that makes this a recommendation tool instead of just a calendar. But look at everything it sat on top of: the five-minute idea, the napkin sketch, the familiar tools, the seventeen rounds on the graphics, the flag that was not a country, the calendar I rebuilt, the data that lied, the database I skipped, the email plumbing, the decision about where the AI belonged, the decision about which one to use, and the days I spent training it to stop being generic.

That is the whole point. The AI is real, and it is powerful, and it is not the hard part. The hard part is everything around it. Understanding the problem. Designing the experience. Choosing your tools with discipline. Cleaning up messy reality. Wrapping the model’s judgment in facts you control. The magic is 5%. The thinking and the craft are the other 95%, and that is the part that decides whether what you build is any good.

The tool is live and free at singla.ai/worldcup. Go build yourself a watch list, and if you start wondering how the rest of it works, now you know.

This is how I approach every build. Start with a real problem. Design before you write code. Respect the unglamorous 95%. Put the AI exactly where it earns its place, and not an inch further.

If you have a problem in your own business that feels like this, specific, real, and quietly shared by a lot of people, that is exactly the kind of thing I help companies build. Let’s talk. I would rather build the next one with you than have you just read about it.