The Dirty Secret of Vibe Coding

You prompt an agent, you get a gorgeous UI in 30 seconds. Then you ask for one more feature, and the app quietly falls apart. Not with a crash — with a silent behavioral failure.

A classic case: you ask for a promo-code feature in your shopping app. The agent builds the input, the Apply button, the success toast. You type SAVE20, the UI says "Discount applied!" — and the new total is never persisted, never rendered, never written anywhere. The screen lies to you.

This is the part nobody talks about in the vibe-coding hype cycle. The failure isn't visible. To find it you have to click around, read logs, and ultimately read the generated code — which defeats the entire point of not reading code.

Why the Usual Suspects Don't Save You

  • LLMs as judges: Ask the agent to debug itself and you get confident hallucinations. Frontier models miss edge cases and misread cross-handler data flows.
  • Unit tests: You're writing more code to check generated code. Non-programmers can't do it, and it rarely covers UI-to-backend integration.
  • Static analysis by hand: Deterministic and accurate, but the learning curve is brutal — you'd need to write CodeQL queries yourself.

There's a fourth option, and it's what this piece is about.

Developer prompting an AI coding agent in a chat interface to build a web app Dev Environment Setup

FlowCheck: Express Intent at the UI, Verify at the Code Level

FlowCheck is a constraint language plus a static analysis pipeline. The core idea: you describe behavior by clicking on your own app, and it compiles that into CodeQL queries against the generated source.

Step 1 — Point at what matters

You give FlowCheck your app's path. It opens your app in a tab with an overlay template:

When I take [action], these update: [component].

You click the actual UI elements — the promo input, the total, the storage layer — exactly how you'd interact with the app anyway.

Step 2 — The constraint becomes formal

The template compiles into a probabilistic constraint:

P(event | condition) = [0, 1]

Read it as: the probability of the event happening, given the condition, is 1 (always) or 0 (never). For the promo bug:

P(w(total) | A(promo-input)) = 1

Meaning: whenever the promo input is applied, the total must be written.

Step 3 — Compile down to CodeQL

FlowCheck parses the constraint's AST to extract the action, the event type (e.g. a write), and the target element. Then it maps those UI components to sources and sinks in CodeQL's relational database of your codebase, and emits two checks:

path_exists      // is there ANY reachable path from the UI action to the write?
all_paths_write  // does the write happen on EVERY execution path from that action?

The second one is the killer. Most silent failures aren't "no path exists" — they're "the path exists on the happy branch but not the conditional one."

Step 4 — Deterministic verification

CodeQL runs the queries. If a constraint is violated, FlowCheck tells you which aspect failed and the exact lines where the data flow breaks. No model in the loop, no hallucination, no false positives in their evaluation.

The Numbers

The authors built four apps modeled on Amazon, Twitter, Airbnb, and Slack via Claude Code, injected 30 subtle real-world data-flow bugs, and wrote constraints for each.

MethodBugs Caught (of 30)
FlowCheck30 / 30 (100%)
Claude Opus 4.7 (best prompt)26 / 30 (87%)
DeepSeek V3lower
Gemini Prolower

The most interesting finding: adding prompt detail didn't always help. With more context, models read the code more thoroughly — and then grew more willing to trust it, rationalizing bugs as intentional design rather than flagging them. That's a failure mode no amount of prompt engineering fixes.

The models consistently broke on cross-handler flows. In the Amazon clone, an "apply promo" action cleared the cart summary while the checkout handler still tried to read it. Neither handler referenced the other, so the model evaluated them in isolation and never traced the flow between them.

Web application UI with a cart and promo code field under static analysis debugging IT Technology Image

Where This Approach Has Limits

Let's be honest about the boundaries, because the paper is honest too.

  • Constraint coverage is on you. FlowCheck catches 100% of the targeted failures — the ones you thought to express as constraints. A bug in a behavior you never specified is invisible to it.
  • Data-flow shaped only. The pipeline is built around source-to-sink tracking. Pure logic errors (wrong discount math, off-by-one in pagination) aren't data-flow violations and won't be caught.
  • JavaScript/TypeScript ecosystem bias. CodeQL support is strongest for JS/TS web apps. If your vibe-coded app is Python or Go backend-heavy, mileage varies.
  • Setup friction. You still need to run the tool, point it at a build, and let CodeQL index the codebase. It's not a one-click browser extension.

Practical Takeaway for Your Workflow

Don't treat this as "replace reading code." Treat it as regression protection for intent. Write five constraints for the flows that would silently cost you money — checkout, auth, persistence, payment state — and run them after every agent iteration. That's where the ROI is.

If you're thinking about how AI-generated pipelines behave at scale, the same lesson applies to infrastructure: verify the data flow, not the vibes. A good companion read is this deep dive on scaling media processing pipelines at Netflix, where the same principle — instrument the flow, don't trust the surface — shows up in a very different context.

And if you want to see what happens when the underlying compute layer gets dramatically faster, this analysis of GPU-accelerated Presto on NVIDIA GB200 NVL72 is worth your time — faster analytics makes verification loops tighter, not optional.

CodeQL static analysis query scanning generated JavaScript for silent data flow bugs Coding Session Visual

The Bottom Line

Vibe coding isn't going away, and neither are silent failures. The winning move isn't "trust the agent more" or "read every line" — it's making intent machine-checkable. FlowCheck's contribution is showing that a UI-level constraint language can compile into deterministic static analysis and beat frontier models at their own game.

What to Learn Next

  1. CodeQL fundamentals — even a shallow understanding of source/sink taint tracking will make you dramatically better at reasoning about generated code.
  2. Property-based testing — the same philosophy (express invariants, let a machine find violations) applied to runtime instead of static analysis.
  3. Constraint specification for non-programmers — this is an open research area, and it's the real bottleneck for vibe coding going mainstream.

You can dig into the full paper and try the tool yourself — the research is the 근거자료 behind everything above, and the repo is open for stars and issues.

This content was drafted using AI tools based on reliable sources, and has been reviewed by our editorial team before publication. It is not intended to replace professional advice.