I create modern, high-performance, and responsive websites using using the latest technologies.
Codenames Annihilator started as a joke idea for a website, which would be able to create perfect clue words for Codenames (with some help from ChatGPT). It quickly evolved into my favorite side project so far, and was my playground for learning RSC along Next.js' app router.
Next.js · Typescript · Tailwind CSS · Drizzle
Vercel · Turso
When designing the app I knew I wanted to include these 3 major features:
What was also important to me, was to have a separate preview environment as soon as possible. Since I knew I'd actually show this website to other people and encourage them to generate some clues, I didn't want to risk breaking the production deployment, just because something would behave differently on my local machine then in production.
Because I was using GitHub actions for deployment, I was able to create a separate workflow for when a pull request was opened, that created a preview build using a separate database instance. It also automatically run any schema migrations, allowing me to ensure that no data would be lost between deployments.
The OpenAI API isn't the cheapest one. I had to somehow limit the per user usage of the clues generator, while also keeping the friction of using the app as low as possible.
My first idea was to use a random words API instead of the OpenAi's one to generate random clues for non signed-in users. These would then be saved to a cookie, allowing the user to experience the flow of the app without the need of signing up. But I quickly realized that this would require me to double the amount of code I'd have to maintain and create for new features, so I gave up on this idea.
What I ended up doing was creating ghost users - users that are created automatically when a non signed-in user generates clues for the first time. These users would still get random words back, but they'd be stored in the database alongside normal users, just with a different flag set. This allowed me to reuse the same code for the generator and flashcards, while also keeping the cost of the OpenAI API in check.