I Let Qwen3.6 Build a Full-Stack App. It Worked. I Wasn't Satisfied.
I gave Qwen3.6-35B a single prompt to build a full-stack web app from scratch. It did. The build passes, the features ship, the tests are green. And I am not satisfied, because the code is duplicated, the architecture is improvised, and it took eight debugging iterations to get here, where a top-tier cloud agent would have taken two.
Quick Take: Qwen3.6-35B + opencode can build a functional full-stack app from a single prompt, but the gap between “works” and “production-ready” is measured in architectural judgment, not raw capability. For prototyping the model is sufficient. For publishable output the refinement gap is still too large.
Table of Contents
- Local LLM coding experiment
- What Qwen3.6 built from one prompt
- The gap between “works” and “good”
- What this experiment measured
- Local vs Claude comparison table
- The verdict: capable but not equivalent
Local LLM Full-Stack Experiment
I gave opencode (my local AI CLI, running on Qwen3.6-35B-A3B) a greenfield task: build an interactive Bitcoin Power Law spiral visualization. Full stack. SvelteKit 5 with runes mode. D3.js for the chart. Multi-provider API (CoinGecko, Binance, Mock). Dark theme. Mobile-friendly. German UI.
No Claude Opus. No Claude Sonnet. No cloud model. Just Qwen3.6-35B via vLLM on my DGX Spark, accessed through opencode. For the occasional frontier model task I use ppq.aiAffiliate link. You support sovgrid at no extra cost to you. See /support. as a no-KYC fallback (paid per query over Bitcoin Lightning), but this experiment was strictly local.
The question wasn’t “can it build this?” That’s trivial. The question was: what does the gap look like when you measure against what a top-tier cloud agent would produce?
This is the same class of evaluation I’ve been running with agent-bench for months now. The pillar article lays out the methodology: deterministic tasks, same-ruler comparisons, no synthetic benchmarks. The difference here is the task domain. Instead of coding micro-tasks (renames, callers, type-checks), it’s a full-stack web app from a single prompt.
What Qwen3.6 Built: Feature List
The app works. That’s the headline.
- SvelteKit 5 scaffold with Tailwind CSS v4,
adapter-static, prettier, eslint - D3.js SVG polar spiral chart with 4 halving cycles, grid circles, halving markers, hover tooltips, animated current position
- Main page with Play/Pause, speed control (1x/2x/5x/10x), timeline slider, progress bar
- Settings page with provider selector (CoinGecko, Binance, Mock)
- API endpoint supporting three providers with different data characteristics
- Custom Node.js HTTP server (
serve.cjs) for production becausevite previewcan’t handle API routes with static adapter - Build passes:
svelte-check0 errors,npm run buildsucceeds,npm run lint0 errors

The final product is a functional demo. It runs. It looks decent. It does what it’s supposed to do.
I am not satisfied with it.
And that dissatisfaction is the entire point of this experiment.
The Gap: “Works” vs “Good”
The difference between what Qwen/opencode produced and what I’d expect from Claude Opus isn’t binary. It’s a spectrum of small compromises that add up.
Architecture: Competent vs Opinionated
Qwen built a working architecture. But it’s improvised rather than opinionated.
The serve.cjs is a custom CommonJS Node.js HTTP server, not wrong, but a workaround for a problem that SvelteKit’s adapter-static should handle. The root cause: adapter-static({ strict: false }) was needed to allow API routes alongside static files, and even then, vite preview can’t proxy API calls. A cloud agent would have either:
- Used a proper SvelteKit endpoint with SSR fallback
- Chosen a different adapter strategy
- Identified the constraint earlier and designed around it
Instead, we got a serve.cjs that duplicates the API logic from +server.ts. Same mock data generation, same fetch logic, same error handling, written twice. Here’s the duplication made concrete:
// src/routes/api/btc/+server.ts (SvelteKit endpoint)
function generateMockData(days: number) {
return Array.from({ length: days }, (_, i) => ({
date: new Date(Date.now() - (days - i) * 86400000).toISOString(),
price: 40000 + Math.random() * 60000,
}));
}
// serve.cjs (production Node.js server — same function, different file)
function generateMockData(days) {
return Array.from({ length: days }, (_, i) => ({
date: new Date(Date.now() - (days - i) * 86400000).toISOString(),
price: 40000 + Math.random() * 60000,
}));
}
Two sources of truth for one endpoint. Change the mock range in one file and you silently break the other. That’s the maintainability debt in concrete form.
This is the same pattern I’ve seen in the opencode setup article and the goose-vs-vibe comparison: local agents produce working code, but the architectural decisions tend toward the pragmatic rather than the principled. Claude Opus would have recognized the adapter-static constraint upfront and designed around it.
Bug Resolution: Trial-and-Error vs Root-Cause
The development session had multiple debugging loops:
- CSS not loading, prerender config missing on layout
- JS files 404, missing favicon.svg, adapter-static strict mode
- No index.html in build, layout prerender = true
useNavigatedoesn’t exist, switched togoto()- TypeScript type errors with IIFE, refactored to let assignment
- ESLint errors on
anytypes, typed asunknown[][] - ESLint errors on
#eachwithout keys, added keys require()errors in serve.cjs, added ESLint override
A cloud agent with access to Opus-level reasoning would likely have identified the prerender constraint before building the first component, chosen the right navigation API on the first try, and structured the API layer to avoid duplication.
Instead, we got 8 distinct debugging iterations. Each one was resolved correctly. But the time cost is real.
This mirrors the pattern from the caveman benchmark: local models can solve individual tasks correctly, but the cumulative debugging overhead is the hidden cost. In the agent-bench framework, this would show up as higher wallclock time per task, not lower pass rate.
Code Quality: Functional vs Clean
The code works. But it’s not clean.
The +server.ts had any types that had to be fixed mid-session. The serve.cjs duplicates API logic. The component structure is flat, no composition patterns, no shared hooks. The CSS uses inline Tailwind classes everywhere instead of extracting reusable patterns.
None of this is broken. But it’s the kind of code you’d refactor in a second pass. A cloud agent would typically produce code that needs less refactoring.
What I Learned
1. The “Works” Threshold Is Lower Than You Think
Qwen3.6-35B can absolutely build a full-stack app from a prompt. Scaffold, components, API, build, run. The bar for “functional demo” is very low for a 35B parameter model.
But “functional” is not “production-ready.” The gap between those two states is where the real engineering happens, and that’s where the model shows its limits.
2. Debugging Is the Real Cost of Local LLMs
The most expensive part of the session wasn’t building the app. It was fixing the bugs. Eight distinct debugging iterations. Each one required:
- Reading the error
- Understanding the root cause
- Finding the right fix
- Verifying it didn’t break anything else
With a cloud agent, many of these would have been avoided by better upfront architecture. The debugging cost is the hidden tax of using a smaller model.
This is the same lesson from the measurement traps article: the visible metric (pass rate, build success) tells only half the story. The hidden cost (debugging iterations, time to fix) is where the real difference shows up.
3. Context Window Is Not Intelligence
Qwen has a 262k context window. That’s massive. But context window size doesn’t correlate with architectural judgment. The model can remember everything in the session, but it can’t reason about the whole system as coherently as a larger model would.
The serve.cjs duplication is a perfect example. The model remembered the API logic from +server.ts but didn’t recognize that duplicating it was a problem. That’s not a memory issue. That’s a judgment issue. Larger context doesn’t produce better architecture, it produces more coherent retrieval of what’s already there.
4. Agent Tooling Matters More Than the Model
opencode is a solid tool. It gives the model bash access, file editing, and a structured workflow. But the tool can’t compensate for the model’s limited reasoning about system-level design.
A cloud agent (Claude Opus/Sonnet) has the same tooling capabilities but better judgment about what to build and how. The difference isn’t in the tools. It’s in the model’s ability to make good decisions with those tools.
This is the same finding from the goose-vs-vibe-vs-opencode comparison: the tooling layer matters, but the model’s judgment matters more. All three agents (opencode, vibe, goose) have similar capabilities, but the quality of output depends on which model powers them.
5. Open Source vs Internal: The Tradeoff Is Real
The app works. Technically, it could be open-sourced on GitHub. The code is clean enough, the build passes, the README covers everything.
I don’t want to open-source it.
Not because it’s bad. But because it’s not good enough to represent my standards. Open-sourcing it would signal a level of polish and reliability that it doesn’t have. And I’m not willing to invest the additional weeks of refinement that would be needed to close that gap.
This is the central tension of local-agent development: the output is often “good enough to work” but “not good enough to publish.” The refinement gap is real, and it’s measured in weeks, not hours.
Local vs Claude Comparison Table
Here’s what the session actually measured. The Qwen3.6 column is observed. The Claude column is an unmeasured baseline, an informed estimate based on prior experience and the architectural patterns documented in the agent-bench pillar, not a controlled run.
| Metric | Qwen3.6 + opencode (measured) | Claude Opus/Sonnet (unmeasured baseline) |
|---|---|---|
| Time to functional demo | ~2 hours | ~30 minutes (estimated) |
| Debugging iterations | 8 | 2-3 (estimated) |
| Architecture decisions | Improvised | Opinionated |
| Code duplication | Yes (serve.cjs) | No |
| Type safety | Fixed mid-session | Enforced from start |
| Satisfaction level | ”Works, but…" | "Ship-ready” |
| Open-source ready | No | Yes |
The Qwen3.6 numbers are real. The Claude baseline is a judgment call, not a measurement. A proper same-ruler comparison would require the identical task run on both models under controlled conditions, the same methodology from the agent-bench pillar. That experiment is on the list. For now, the two-hour vs thirty-minute gap is directionally correct and worth taking seriously, even if the exact number hasn’t been verified.
The Verdict
Qwen3.6-35B + opencode is capable. It built a full-stack app from scratch. It debugged its own errors. It produced a working product.
But it’s not equivalent to Claude Opus or Sonnet for full-stack development. The gap isn’t in basic capability. It’s in architectural judgment, decision quality, and the ability to avoid mistakes that require debugging later.
For prototyping and experimentation, Qwen3.6 is more than sufficient. For production-quality output, the gap is still significant.
I’m not publishing this app as open source. Not because it doesn’t work. But because the refinement gap is too large to close without an investment that isn’t justified for a demo project.
The real value of this experiment isn’t the app. It’s the measurement. I now have a concrete data point for what local-agent development looks like at the 35B parameter level. And that data point is: functional, but not polished. Capable, but not production-ready.
Local vs Cloud agent output
The gap between 'works' and 'production-ready' is where the real learning happens