This tool was born out of a period of waiting.
I remember submitting my application for permanent residence to the official website early in 2025. The preparation process was incredibly tedious. It involved numerous documents and required signatures from both my company and my landlord. I originally assumed the whole process would wrap up in a few months, but it dragged on for more than half a year before I received any feedback.
It was a system-generated email with ugly formatting and full of errors. Only one line appeared to be a reply from a visa officer, which seemed to have a faint “human touch.” It stated that I had to submit proof of passing the naturalization test within two weeks, or my application would be forcibly withdrawn. I was astonished by this long-awaited response because the official website clearly stated the requirements. To prove integration into Germany, one could provide a German university degree or a naturalization test certificate. I stared at that word “or” for a long time to make sure I wasn’t seeing things. Later, I appealed to them with legal statutes and website information, but the response remained the same: “we need the test certificate.” It felt like talking to a wind-up machine, and it was even worse than a reply from GPT.
I eventually figured that arguing was a waste of energy, so I decided to let it go and just take the exam. I checked online and found the earliest available test was three months away, likely because it was fully booked. The registration method was also extremely retro, requiring an in-person visit. I can understand offline exams to verify identity and prevent cheating, but the fact that registration still required a physical presence in 2025 truly baffled me.
About a month before the exam, I started slowly gathering information related to the test, such as rules and question types. Simply typing keywords into Google revealed a pile of question banks, including official ones, community-made ones, and PDF collections.
I browsed through the top-ranked websites and found that none of them really met my needs.
First, logically speaking, the naturalization test is for non-Germans, yet not a single website offered multi-language support. My mother tongue is Chinese. Although I have learned German and can practice in German without issues, I sometimes want to see translations to ensure my understanding is correct. I could not find any website that supported this.
Second was the practice method. Perhaps inspired by the official website, almost all sites used a “one question per page” format. You select an answer, the result pops up, and you have to click a button to go to the next question. I couldn’t see the results of previous answers, nor did I have a global overview. If I wanted to review incorrect questions specifically, I had to click back one by one, which was very inconvenient.
Finally, there was the style. In an age where web and AI technologies are changing daily, the styles of most webpages were surprisingly primitive and cluttered. There were all sorts of strange styles, fonts, and inexplicable images flying everywhere. I just wanted to practice questions, so giving me the question, the answer, and the result would suffice. I didn’t understand why it had to be so flashy and complicated.
After some simple research and thought, I realized the technical implementation wasn’t complex, so I immediately decided to build a practice tool myself. The medium would be a website because it is cheap enough, simple, clean, and satisfies the need to “practice anywhere.”
The overall approach was as follows. First, design the most basic data structure, which naturally supports multiple languages. Second, fill in the data and add the translations. Next comes building the website by adding styles and core logic functions. Finally, deployment. Although the description is sequential, the actual work was done in parallel. For instance, “website deployment” and “data collection” were completed simultaneously. I really enjoyed the moment the website came to life.
Initially, I used Google’s AI Studio, which allows free use of Gemini 2.5 Pro. This model is very friendly towards long contexts. For general data processing tasks, I would provide the desired input and output formats and ask it to return Python or Bash scripts. In every prompt, I requested it to output scripts that were as simple and easy to understand as possible. This ensured I could quickly browse and comprehend the code, which is friendlier for debugging and gave me a greater sense of control.
The original data language was German, and the format was JSON. For the multi-language part, I used additional scripts combined with the Qwen/Qwen3-Max model via OpenRouter. It is cheap and has excellent support for multiple languages.
For the domain and deployment, I used Cloudflare. The domain price isn’t too high, and it allows one-click configuration for the deployed platform. The platform used is their Pages service. Whenever code is written, the pipeline triggers automatically to generate the static site and deploy it. It also provides an extra branch preview function, which is very developer-friendly. Incidentally, my blog uses similar technology.
With the basic platform set up and data filled, I could finally officially start designing the website. The framework used is AstroJS. It has the simple virtues of HTML and JS while supporting modularity and compatibility with other modern frameworks. I feel it is a framework with a very “Eastern” flavor, inclusive and eclectic.
When developing web pages, I hold a belief that code and files must remain concise. If something can be done in one file, I will never put it in a second one. Too many modules and files make the application complex and make it difficult for me to understand. Additionally, if I want to add new features or fix bugs, I can send the code containing the full context directly to Gemini. The model can quickly locate and solve the problem.
After a dozen rounds of dialogue, the tool began to take shape. It included the most basic styles, could display all questions, and allowed clicking to show correctness. In the days that followed, I successively added language translation, answer resetting, result previews, and other functions. I was very satisfied with it. I could open the browser and quickly practice questions even while on the subway or bus.
As I used it myself, I experienced some inconveniences, such as the lack of a dark mode and a favorites mode. The latter acts like a bookmark. If I got a question wrong, I could simply mark it. Even if the answers were reset, I could jump back and do it repeatedly to reinforce my memory.
Later, prompted by my friend’s inquiry, I bought a one-month membership for Claude Pro out of curiosity, costing about 20 Euros. I wanted to experience if the “Vibe Coding” talked about online was truly that amazing. After using it, I want to say yes, it is indeed amazing. It can call tools and modify code based on just a few of my sentences to solve problems. For the first few days of the membership, I was addicted to using it and couldn’t wait to open it and modify the webpage as soon as I got off work.
However, the “thrill” came at a price. After requesting it to refactor the code several times, I found that I gradually lost control over the code details. The code in my memory used to be line by line, precisely coupled together, but now it had turned into a fog. I had to dive into the fog to barely make out the location of the code and how it functioned. Although it would confidently tell me that the code’s current state was the most concise and clean, I could still occasionally spot some very verbose logic. It would insist on going from A to B and then to C, finally solving the problem through C, when doing A directly would have sufficed.
Perhaps my own expression and train of thought were unclear, causing its work to become very arbitrary at times. For example, I am used to handling all styles with TailwindCSS rather than writing extra custom CSS. I feel that whether it is code, a project, or a team, there is a tacit understanding and habit. Reading the “air” they transmit is very important. This thing, which is like physical inertia, saves a lot of communication costs. However, the model doesn’t understand this. It is oriented by prompts and results. It cannot read the “inertia” of the current situation, but humans can.
Often, I am not even aware of my own intent. I feel this might be an opportunity to correct the model through its mistakes and use this feedback to gradually clarify my own intent, then express it through natural language. But the point of this back-and-forth discussion lies in learning, not in completing a very solid tool or product through engineering. I can understand why people who don’t know much about code are fanatical about it because it has a sense of magic. I can also understand why people who know code sometimes don’t like it as much because it breaks away from your control and tricks you into thinking things are done. In reality, it hasn’t followed the author’s true intent. After all, you can see that the code it writes sometimes really isn’t that great.
In this small project, I strove to find a balance between adding new features and learning code (or maintaining control over the code). I rate myself as doing okay. Although I can’t write a lot of the logic code, I at least know roughly how it operates, which is enough to identify the location of small bugs.
After finishing the website tool, I also completed the exam, so I had time to further optimize and promote this tool. I specifically invited my friend to be the Product Manager for this micro-project. She tried the tool and asked friends to try it too. The feedback received was that the page layout was poor and the color indicators were unclear. Crucially, the website had no memory function, so answered questions would disappear upon refreshing the page.
To achieve better teamwork, we used Trello as a Kanban Board. However, since we were still figuring things out, the scale and boundaries for creating tickets weren’t defined very clearly, so the practice was sporadic. Sometimes when encountering very small bugs, I would skip them or merge several to handle them at once, bypassing the ticket process.
Apart from improving the user experience, the most important thing was promotion. I knew promotion was vital, but due to my personality—especially as someone used to writing programs—I felt a bit shy about strutting around the internet showing off what I had made. Therefore, I had never seriously done any promotion. To shift my mindset, I redefined the action with a different phrase. I didn’t call it “advertising” but rather “helping others.” This made me feel much better.
My intention was to help others, specifically those in a similar situation to mine. With this belief, subsequent tasks seemed much more natural. I wrote a short piece of copy and made some screenshots, placing them in a photo template of a hand holding an iPhone. It looked rough, but it still seemed somewhat professional.
The results of the promotion are showing slightly, but it still lacks fire. We still need to learn more about search SEO optimization and recommendation mechanisms.
Now, this tool runs stably with normal logic. Although there are some pop-up ads, they absolutely do not affect usage. In addition to using tools to ensure the questions are up-to-date, we also plan to add more useful extra information, which will also support multiple languages.
Of course, even though I have taken the exam, regarding the permanent residence status, I still need to submit again, pay again, and wait again. I expect everything will drag on until next year before the dust finally settles.