AI (whatever that means) is all the rage now. While I have been using Claude Code at work for a few months, I have been reluctant to use such tools for my side projects - I am unfortunately a perfectionist, and not knowing every bit (pun intended) of my projects does not feel right. However, that means it takes ages to get anything done, because it is never perfect, and I never finish anything, and I drop the projects after a few months, and, and, and. Bummer.

Back to last week.

I have been learning Korean for a few months, and something that constantly botters me is writing Hangul on my laptop. While Ubuntu can give you a nice virtual keyboard, it is clunky. Changing my keyboard layout to Korean does not really work, because my physical keyboard is kind of ASCII (English EU, or something like that), and well, I don’t want to physically modify it, so it is hard to know what I am typing. An external keyboard is a no go because my apartment is already cluttered. What is left are virtual keyboards on the web, but they are either ugly or riddled with ads. 🤮 As a programmer, I had no other choice other than create my own virtual Korean keyboard.

I started my project the old way, bootstraping a React + Typescript project, and researched how keyboard layouts in Korea look like. The standard layout is called Dubeolsik, and matches the layout I had setup in my Samsung phone, so it seemed like a sensible choice. Just like this blog, I used PicoCSS to give it a bit of style, and drafted the UI. I hardcoded the mapping between the Latin characters and their Hangul counterparts (jamo, thankfully there is an Unicode Block for them), and called it a day.

And then the problem began. Korean syllables are composed either of a consonant and a vowel (CV) or a consonant, a vowel and a consonant (CVC). But wait, there is more! Korean also has double vowels, double consonants, and double final consonants. That means we can have more than 11000+ combinations, and that is a lot. I imagined that you could do some magic with the Unicode values for each character to derive a syllable (like the formula to capitalize/lowercase ASCII chars everyone learns in CS101), but never did the research. I also imagined that I could use a state machine to build the syllables - the logic is that a syllable is either open or closed based on the previous jamo and the current one, given that all syllables have to be in a state that follows the CV/CVC pattern.

But as a father of two, with barely any free time, this went on veeeery slowly - until I turned to my friend Gemini (because I got a Google One subscription). And guess what? In a couple of hours I could deliver the whole thing, the way I wanted it. Here is how it looks like:

Initial prototype

You can also see the real thing (and break it) here: kr.guh.me . Note that it does not work on mobile, and that the title is not a real Korean word, but a joke for myself. 🤡

To finally ship my project, I did have to relinquish some control over the code - after all, I did not write it myself - but I still reviewed it, and, most importantly, I kept control of the specification and the architecture of the project. This means that had to feed the agent a detailed specification of each feature ( including test cases to validate), and had to setup a workflow (skill) of how I wanted it to work. In this case, I chose that my agent use Test Driven Development, first implementing a harness to verify the desired behavior, and then moving to the application code itself. This also included the tools the agent should use (tsc for type checking, eslint for linting, and vitest for testing). Some time later, I also did setup the Chrome MCP server to let the agent use the real thing when I hit a weird edge case.

Basically, I used the agent to augment my capabilities and to automate boring work, while still retaining ownership of the artifacts produced by it. And before the pitchforks are raised: when I say boring work, it is not that I don’t find joy in writing code, but that my real reward is solving a problem, and code is just a means to that.

Moreover, and perhaps most importantly, vibing let me:

  1. validate the editor implementation using a state machine without spending ∞ hours.
  2. find the goddamn documentation for the Hangul block in the Unicode Standard v17. This was particularly enriching, because I got to know the logic behind the design of the block (chapter 18.6), and that the smart guys that implemented it even gave us a formula to produce syllables from individual jamo (chapter 3.12).

Speeding up my learning and execution processs are very good reasons for me to continue using AI tools - while we cannot ignore the economical, societal, and environmental impacts of AI, one cannot deny its utility. Answering my post tile, “to vibe is indeed the answer”.