maintainable swe vs vibe coding
this blog is a tiny example of how i like to use coding tools without falling into pure “vibe coding.” the goal: keep things maintainable, move fast, and let genai help where it actually shines.
reality check: fully autonomous coding isn’t there yet. you still need to touch code, make choices, and keep your system coherent. and if you over-index on genai in the wrong places, it can even slow you down (good discussion here: on when genai helps vs hurts).
so the question is: where do i want genai in the loop?
for me, two categories are obvious wins right now:
- repetitive tasks with clear examples/analogies
- rapid prototyping that doesn’t need deep technical depth (yet)
the framework i use is simple: map recurring work to these buckets — or design the work so it naturally falls into them. if a task keeps coming back and is pattern-shaped, let genai draft it. if i need to explore a shape quickly, let genai sketch it. but the architecture, naming, interfaces, and constraints stay deliberate and simple.
example: this blog
- i knew i’d want to generate content from time to time, ideally straight from a model. i didn’t want to get stuck redesigning pages or worrying about breaking layouts.
- the solution: make the source of truth dead simple and model-friendly. markdown as the interface. static site generation to keep things fast and predictable.
- stack: astro + markdown. build the foundation once (routes, layout, styles). after that, every post is just a
.md
file — or copy/paste from a chat. no extra hoops. - bonus: plain markdown is easy for search engines and llms to parse. it can live in the sitemap as-is and likely gets picked up with less noise. that might help with llm seo over time.
maintainability beats vibes because it compounds. simple conventions, small files, predictable flows. then add genai where it reduces friction, not where it creates mystery. that’s the balance i’m aiming for here.
example: mirai (talkwithmirai.com)
- early on we struggled to get up to speed on the app.
- a big pain point: underwhelming performance of otherwise great coding models like claude sonnet 4 when working in a flutter environment.
- we blamed it partly on less training data around dart and flutter (fingers crossed this improves with flutter 3.35.0 — release notes — which also introduces mcp server support; hopefully this reduces friction and increases genai capability here).
foundation: we went a bit old-school. i coded a clean, ddd-style flutter app with riverpod for state management at the core.
even with that, genai often didn’t pick up the patterns well enough to produce maintainable code for new features.
this is where category 2 helps: rapid prototyping. so you build a process optimized for speed while hedging the downside on maintainability — quick experiments up front, strict containment, deliberate integration later. rigid architecture too early slows you down when the feature is still uncertain; you don’t want to pay the structure tax before validation.
so we used what i call “contained chaos”
- keep the clean ddd layers (infrastructure, domain, application, presentation).
- add a
features/
directory with subfolders per subfeature. - when a new, high-iteration feature appears, vibe-code it inside its own subfolder. only integrate via existing public apis, or build local, throwaway glue.
- all changes must be contained in that folder — guaranteeing no breakage for the stable core.
- once validated and stable, port the distilled parts into the proper ddd layers.
- if not validated, delete the folder. gone. minimal blast radius.
caveats: not every feature can be perfectly isolated, but you can usually get close by designing against stable interfaces. this keeps speed high without sacrificing long-term maintainability.