← EasyTool.me

Going Back to Writing Code by Hand

Published: 2026-05-11

EasyTool.me9 min read中文版

A developer archived their 234-commit project built entirely on Claude vibe-coding sessions and went back to writing code by hand. The HN front page post by k10s.dev struck a nerve with the developer community – 52 points in under an hour at the time of writing, and the discussion is still heating up.

This isn't another "AI is bad" take. It's a nuanced post-mortem of what happens when you let AI coding tools drive the architecture of a project for 7 months, and why the author decided that writing code by hand was the only way forward.

"AI writes features, not architecture. The longer you let it drive without constraints, the worse the wreckage gets. The velocity makes you think you're winning right up until the moment everything collapses simultaneously."

The Story: k10s and the God Object

The author built k10s, a GPU-aware Kubernetes TUI dashboard (think k9s for NVIDIA cluster operators), entirely using Claude vibe-coding sessions. For the first few weeks, it was magic. Each feature landed clean and fast. The basic k9s clone – resource views, command palette, live updates, vim keybindings – took maybe 3 weekends.

Then came the GPU fleet view – the core feature that justified k10s's existence. Claude one-shot it. FleetView struct, tab filtering, allocation bars, color-coded GPU status. It looked beautiful.

Until the author typed :rs pods to switch back to the pods view. Nothing rendered. Stale data. Corrupted state.

The god object had consumed itself.

What the author found when they sat down to read model.go – all 1690 lines – was horrifying. One struct held everything: UI widgets, K8s client, per-view state for every feature, navigation history, caching, mouse handling. The Update() method was a 500-line function with 110 switch/case branches. Keyboard shortcuts had conflicting meanings: the s key meant "autoscroll" in logs, "shell" in pods, and "shell into container" in containers — all in one flat switch statement.

The author counted nine manual = nil cleanup lines scattered across the file. Miss one, and ghost data from the previous view would bleed through.

The Five Tenets from the Wreckage

The author distilled 7 months of pain into five key lessons that apply to anyone using AI coding tools seriously.

1. AI Builds Features, Not Architecture

Every prompt delivered a working feature. The problem was that each feature was implemented in isolation, with no awareness of the 49 other features sharing the same state. The AI optimized for "make this work right now" at the expense of long-term maintainability.

The fix: Write the architecture yourself before any code. Put concrete interfaces, message types, and ownership rules in your CLAUDE.md or agent.md. The AI will follow these if you write them down. It just won't invent them for you.

2. The God Object Is the Default AI Artifact

AI gravitates toward single-struct-holds-everything because it satisfies the immediate prompt with minimal ceremony. Every new feature adds another field, another if-branch, another special case. String comparisons as type discriminators (m.currentGVR.Resource == "nodes") instead of proper polymorphism.

The fix: Each view should be a separate struct implementing a View trait/interface. Each view declares its own key bindings. Adding a view means adding a new file, not modifying existing ones.

3. The Velocity Illusion Widens Your Scope

This is psychological and it's the most dangerous trap. When each feature feels free (the AI does it in one session), you say yes to everything. The author started building a GPU-focused niche tool and ended up building a general-purpose k9s clone. The architecture couldn't support the scope expansion.

The fix: Write a vision doc that explicitly states who you're NOT building for. Put scope boundaries in your CLAUDE.md. Vibe-coding gives you infinite line budget but your complexity budget is still finite.

4. Positional Data Is a Time Bomb

AI loves flattening structured data into []string because it satisfies any table widget immediately. Column identity becomes purely positional: ra[3] means "Alloc" because a comment says so. Add a column? Every sort, every conditional, every render is silently wrong. The compiler can't help because it's all []string.

The fix: Never flatten structured data. All data flows as typed structs until the render call. Column identity comes from struct field names, not array indices.

5. AI Doesn't Own State Transitions

The article shows a textbook data race: a closure spawned inside Update() mutates Model fields from a goroutine while View() reads the same fields on the main goroutine. No mutex. No message passing. It worked 99% of the time, and corrupted the display 1% of the time.

The fix: All mutations to render-visible state happen on the main loop. Background workers produce data and send it as a message. The main loop receives and applies it atomically.

When AI Coding Is Useful vs. When It's Not

The article doesn't advocate abandoning AI tools entirely. It's about knowing when they help and when they hurt.

AI Coding Works Well Write by Hand Instead
Single-file scripts & prototypes Multi-module architecture design
Boilerplate generation (CRUD, tests, types) State management & ownership boundaries
Exploratory code — "what does this API look like?" Concurrency & data flow design
Familiar patterns in a known codebase New abstractions & interface decisions
One-shot utility functions Code where correctness under edge cases matters
Key insight: The hidden cost of AI coding isn't the subscription price. It's the time spent reviewing, debugging, and refactoring AI-generated code that looks correct but has subtle architectural flaws. When reviewing AI code takes longer than writing it yourself, the productivity math flips.

The Real Cost: Cognitive Debt

This is closely related to the concept of cognitive debt — the accumulated mental overhead of code that doesn't match its architectural promises. AI-generated code is particularly prone to this because it optimizes for local correctness, not global coherence.

The author decided to rewrite k10s in Rust, not because Rust is inherently better, but because it's a language they can "steer" — their instincts for what feels wrong are more developed there. As they put it: "The AI hands you plausible-looking code. You need a nose for when it's garbage."

This resonates with recent discussions about AI-generated pull requests flooding open source. When developers submit AI-generated code they don't fully understand, the review burden shifts to maintainers. The same pattern applies to personal projects: the "review" phase is you, debugging ghost data at 2 AM.

What Should You Put in Your CLAUDE.md?

The author's recommendations for preventing AI architecture collapse:

For more on structuring AI coding workflows, check out our guide to coding agents in production and Addy Osmani's Agent Skills approach.

Not Anti-AI, Just Honest About the Tradeoffs

The most important takeaway from this story is that it's not anti-AI. The author is clearly a power user of Claude Code and AI coding tools. They experienced the 10x velocity firsthand. But they also hit the wall that every serious AI-assisted developer eventually hits: AI ships features. You own the architecture.

The decision to go back to writing code by hand isn't a rejection of AI. It's a recalibration. Use AI for what it's good at (features, boilerplate, exploration). Own the architecture yourself. And when you find yourself saying "yes" to every feature because each one feels free, stop and ask: can the architecture survive this?

The author is rewriting k10s from scratch — by hand for the architecture, with AI for the implementation details. That's the pragmatic middle ground. As they concluded: "The instinct is the one thing vibe-coding can't replace."