Vibe Coding a Desktop App: The Experience

2026-02-09 12:00Edit this page

I’m a backend developer who primarily works with C, Lua, OpenResty, and Perl. C#, Swift, WinUI 3, Windows desktop development — all completely foreign territory to me.

I wanted to run an experiment: can you ship a publishable desktop app by relying purely on vibe coding in a tech stack you’ve never touched?

The key phrase here is “never touched.” I don’t know C#, I don’t know Swift (the original codebase), I don’t know XAML, and I’ve never built a Windows desktop app. I wanted to strip away all my domain expertise and experience vibe coding — AI agent-driven development — in its purest form.

The result: 24 days, from zero to Windows Store listing, 19 translation services, 715 test cases.

Tools

Only two AI tools were used throughout the entire project:

  • Claude Code — 100% of the coding work (writing code, research, debugging, refactoring, testing, documentation)
  • GitHub Copilot Review — PR-level code review

No Cursor, no Windsurf, no ChatGPT. The editor was VS Code, but I barely touched the code manually.

The split was roughly 90% AI : 10% human. My 10% went to directional decisions (WinUI 3 vs. Tauri, feature prioritization), having Claude Code explain code and approaches then picking the reasonable one, final review, and providing hints when the AI got stuck.

Initially, I ran Claude Code on WSL. The experience was quite fragmented: the AI would modify code in WSL, then I’d sync it to a Windows directory and compile in VS Code. Switching between the two environments constantly was inefficient. Later, I installed Claude Code directly on Windows, and the entire workflow became much smoother — edit, compile, and run in one place. This was an important factor in the productivity gains that followed.

Origin and the Decision

Easydict is a popular translation dictionary app on macOS. I wanted to port it to Windows.

The initial idea was simple: keep the Swift backend code and only build a Windows UI frontend. I used vibe coding to evaluate several approaches and narrowed it down to two, developed in parallel: WinUI 3 (Microsoft’s official Windows desktop UI framework) and Tauri (a cross-platform framework based on Rust + Web technologies).

After pushing both approaches forward, a common problem quickly emerged: the backend code also had to be fully ported. Keeping the Swift backend was impractical — either the dependency chain became extremely complex, or the results on Windows were poor.

Then the Tauri approach hit a wall first: the build environment was too complex, and I could never get a POC sample running. Meanwhile, WinUI 3 had already produced a working demo. So I dropped Tauri and went all-in on WinUI 3.

For the backend, I chose .NET 8 (an LTS version), which felt very smooth on Windows. The whole C# + WinUI 3 + .NET 8 stack is truly a first-class citizen in the Windows ecosystem.

During the POC phase, I created a subdirectory directly inside the Easydict macOS source repository1. Once the POC was running and the approach was validated, I created a completely independent project, detaching from the original source repository.

Prototyping and Building Features

I gave the AI agent almost full control over the UI design. My only requirements were: reuse the original Easydict icon assets where possible, keep the interface clean and minimal, and support Dark/Light themes. The result was satisfactory — what you see today. As someone who knows absolutely nothing about XAML, the outcome exceeded my expectations.

After the prototype was running, I started stacking features one by one. The key strategy at this stage: commit each feature individually with Git. One commit per feature, preventing later changes from breaking earlier ones. This is basic instinct for a backend developer, but it’s especially important in vibe coding — the AI sometimes accidentally breaks existing functionality when adding new features.

Once two or three features were working properly, I started adding test coverage. On 2026-01-20, I added the first unit test framework (xUnit + FluentAssertions).

Parallel PR Development

After pushing the code to GitHub, I started using a PR workflow to develop features in parallel. That’s when I discovered an unexpectedly good combination: GitHub Copilot Review + Claude Code work remarkably well together.

My development loop looked like this:

Claude Code writes code and submits PR
    → GitHub Copilot Review finds issues, posts review comments
        → Claude Code reads review comments and resolves them
            → Push
                → Another round of review
                    → Loop until approved

Code quality was generally well-maintained through this loop. And because multiple PRs were running in parallel, while waiting for Copilot review or Claude Code to resolve comments, I’d switch to the next feature’s PR. Very efficient.

Not much domain knowledge was needed throughout this process. I had zero knowledge of C# and the original Swift code. I’d have Claude Code explain things to me and pick the approach that sounded reasonable. Occasionally I’d apply general software engineering intuition — “this design is too tightly coupled” or “this should have an interface abstraction.”

Release Journey

v0.1.0 — First Release (Day 5)

5 days after the project started, the first usable version was released. On the same day, I set up CI/CD (GitHub Actions), merged the first PR (#1), and had a basic automated build and test pipeline running. This pace would be hard to imagine in traditional development — I still didn’t know C#’s basic syntax.

v0.2.0 — The Turning Point (Day 12)

12 days after the project started, I submitted release builds to two distribution channels simultaneously: Windows Store (MSIX format) and WinGet (Portable ZIP format).

This version was a major turning point. It meant the app was on a real trajectory — end users could install it through normal channels, and I could start collecting real user feedback instead of building a hardcore tool that required developers to compile locally.

But the release process was far from smooth. This version went through 17 Release Candidates (RCs), from rc1 to rc17, spanning 2 days. The main pain points were in MSIX manifest configuration: MinVersion requirements, architecture support configuration2, icon asset specification requirements, and Store submission compliance checks. These issues surfaced one after another, and each small fix required repackaging, testing, and resubmitting — hence the 17 RCs.

The Blurry MSIX Icon Saga

After packaging as MSIX, a stubborn issue appeared: the Start Menu icon was extremely blurry. Oddly, the .zip portable version had no such problem — only the MSIX-installed version was affected. It took multiple version iterations to resolve. The root cause was that MSIX packages have special size and format requirements for icon assets, different from how regular .exe icons are handled.

Testing and Performance

To improve stability, a multi-layer testing system was gradually established: unit tests (Day 4), integration tests (Day 12), UI automation screenshots (Day 14), and UI regression tests (Day 15). With these in place, my local verification workload dropped significantly — I could just look at automated screenshots in CI to determine whether a change caused visual regression, instead of manually running the app and checking each feature.

In the later stages, I started focusing on performance. I added performance regression tests to monitor the latency of hot paths like mouse-select-to-translate and application startup. However, the test framework can’t yet intuitively prove that a given change hasn’t impacted performance — benchmark stability in CI environments has too much variance. This needs more research.

Where AI Exceeded Expectations

Two aspects of the AI’s performance clearly exceeded my expectations.

The first was architecture design. The overall project structure proposed by the AI was remarkably sound: the solution was split into Easydict.WinUI (UI layer), Easydict.TranslationService (business logic layer), and Easydict.SidecarClient (IPC layer); the streaming translation architecture used SSE + IAsyncEnumerable<string>, the idiomatic C# approach for streaming data3; the Translation Service inheritance hierarchy was cleanly designed: ITranslationServiceBaseTranslationServiceBaseOpenAIService, where adding a new OpenAI-compatible service only requires extending BaseOpenAIService and providing Endpoint/ApiKey/Model. Even if I knew C#, these architectural decisions would probably have taken me several refactoring cycles to reach this level of design quality.

The second was debugging ability. When bugs appeared, the AI could locate and fix issues on its own with a surprisingly high success rate. This was especially true for WinUI 3-specific pitfalls (thread dispatching, COM object lifecycles) — issues that would have cost me significant time searching through documentation and Stack Overflow.

Toolchain Pain Points

Beyond the code itself, the toolchain caused more pain than the code.

The entire MSIX packaging, signing, and Store submission pipeline was extremely complex. Every configuration item in the manifest could potentially cause a packaging failure or Store review rejection. Most of the effort behind v0.2.0’s 17 RCs was spent here.

The GitHub Actions Windows runner environment also had many subtle differences from the local development environment: different Windows App SDK versions and installation states, WinUI tests requiring a graphical environment that mostly fails on headless CI, and MSIX packaging on CI requiring additional toolchain configuration. The AI could help resolve these issues, but it often took multiple attempts. The AI’s solutions are based on documentation and general knowledge, while the actual CI environment is a black box — you can only debug through the push → check logs → fix → push again cycle.

Conclusions and Advice

What is vibe coding best for? Exploring unfamiliar technology domains and rapidly prototyping. This is vibe coding’s greatest value today. Going from zero to Store listing in 24 days — without AI, given my zero-knowledge baseline in C#/WinUI, this timeline would have been at least 5-10x longer.

But foundational engineering skills are still essential. Even though 90% of the code was written by AI, the remaining 10% of human intervention was still critical. That 10% wasn’t about writing code — it was Git workflow (per-feature commits, parallel PRs, branch management), CI/CD mindset (establishing automated pipelines early), testing strategy (knowing when to add tests and at what level), release strategy (shipping to users early to gather feedback), and architectural judgment (choosing the more reasonable approach among the AI’s proposals). These are all universal software engineering skills, independent of any specific language or framework. Vibe coding lowers the barrier of the tech stack, but it doesn’t lower the barrier of engineering discipline.

A few pieces of advice for developers wanting to try vibe coding:

  1. Pick a domain you don’t know but want to learn — vibe coding’s greatest value isn’t writing code you already know how to write, but helping you do things you can’t
  2. Establish Git + CI + tests early — this is your only safety net. AI makes mistakes, and those mistakes are often hard to spot
  3. Use PR review tools as quality gates — AI-written code needs another AI to review it; humans make the final call
  4. Ship early — don’t wait for “perfect.” Deliver a minimum viable version first
  5. Maintain your sense of direction — AI can do 90% of the execution, but only you can make that 10% of directional decisions

Project Data

Key Metrics

MetricValue
Project Duration24 days (2026-01-16 ~ 2026-02-08)
Total Commits422 (master: 220)
Pull Requests~46 (squash merge)
Releases12 (v0.1.0 ~ v0.3.4)
Release Tags (incl. RCs)51
Source Code (C# + XAML)19,554 lines
Test Code17,474 lines
Source:Test Ratio1.12 : 1
Translation Services19
Test Methods715 ([Fact] 695 + [Theory] 20)
Source Files73 .cs + 9 .xaml
Test Files93 .cs
CI Workflows9

Code by Module

ModuleC# LinesXAML LinesTotal
Easydict.WinUI (UI layer)11,1911,85913,050
Easydict.TranslationService (business layer)5,9305,930
Easydict.SidecarClient (IPC layer)574574
TranslationService.Tests9,2189,218
WinUI.Tests3,3243,324
UIAutomation.Tests2,4662,466

Milestone Timeline

2026-01-16  Day 0   Project started, first commit
2026-01-20  Day 4   Unit test framework added (xUnit + FluentAssertions)
2026-01-21  Day 5   v0.1.0 released + CI/CD + first PR (#1)
2026-01-25  Day 9   MSIX packaging + WinGet release workflow
2026-01-26  Day 10  Windows Store submission begins (17 RC iterations)
2026-01-28  Day 12  v0.2.0 released — turning point (Store + WinGet dual-channel)
2026-01-30  Day 14  UI automation tests + screenshot verification
2026-02-01  Day 16  v0.2.2 + UI regression tests
2026-02-02  Day 17  v0.2.3 + mouse-select-to-translate + Copilot Review Loop
2026-02-03  Day 18  Performance benchmarks
2026-02-04  Day 19  v0.3.0 (Inno Setup EXE installer + GitHub Pages website)
2026-02-06  Day 21  v0.3.1 / v0.3.2
2026-02-08  Day 23  v0.3.3 / v0.3.4

19 Translation Services

Google Translate, Google Web, Bing, DeepL, OpenAI, DeepSeek, Gemini, Groq, Zhipu AI, GitHub Models, Doubao, Volcano, Caiyun, NiuTrans, Linguee, Youdao, Ollama, Built-in AI, Custom OpenAI


  1. This made it easy to reference the original Swift code at any time, letting the AI compare and understand the business logic and translation service implementations. ↩︎

  2. Eventually dropped x86, keeping only x64 and arm64. ↩︎

  3. As someone who doesn’t know C#, I could never have proposed this approach. ↩︎

Unless otherwise stated, articles on this blog are licensed under the Creative Commons Attribution 4.0 International License. Please credit the original author and source when sharing.


Tags: programming vibe-coding ai

No comments yet

Leave a comment

Creative Commons © 2013 — 2026 xiaocang | Theme based on fzheng.me & NexT | Hosted by Netlify