Vibe Coding Without the Technical Debt: A Python Guardrail Pipeline That Catches What the AI Missed An engineer built a Python guardrail pipeline that runs AI-generated Flask code through ruff, mypy, bandit, detect-secrets, vulture, pip-audit, pytest and Hypothesis to catch issues that vibe-coded output misses. Testing a deliberately flawed orders API module, the pipeline surfaced 16 problems, though three went unflagged by any tool, and the checks were wired into pre-commit and GitHub Actions. The author argues AI-written code is not unsafe by default but not safe by default either, so every AI change should pass the same checks expected of a new hire. Andrej Karpathy coined “vibe coding” in a post on X on February 2, 2025: you describe what you want, accept what the model writes, and “fully give in to the vibes.” By November, Collins had picked it as its Word of the Year. Adoption followed the hype: in the 2025 Stack Overflow Developer Survey, 84% of respondents said they use or plan to use AI tools, yet more of them distrust the accuracy of those tools 46% than trust it 33% . So is vibe coding bad? The research on AI generated code vulnerabilities gives a consistent answer: the code is not bad by default, but it is not safe by default either. The fix is not to stop using the assistant, but to make every AI-written change pass the checks you would demand from a new hire. In this post we take one small, realistic “vibe-coded” Flask module, run a guardrail pipeline over it, look at what each tool caught and what it missed, fix the code, and wire everything into pre-commit and GitHub Actions. What we will do: Versions pinned in requirements-dev.txt : Python 3.13.18, Flask 3.1.3, ruff 0.17.3, mypy 2.4.1, bandit 1.9.4, detect-secrets 1.5.0, vulture 2.16, pip-audit 2.10.1, pytest 9.2.0, pytest-cov 7.1.0, Hypothesis 6.170.13, pre-commit 4.7.0. Every output block below is copied from a real run. The prompt was the kind you type in a hurry: “Flask API for orders: list a customer’s orders with pagination, apply a percentage discount, notify a webhook.” I wrote the result by hand to reproduce the patterns the studies above keep finding, so every issue here is deliberate, but none of it is exotic. It runs and it demos well: python import json import os import sqlite3 import requests from flask import Flask, jsonify, request app = Flask name DB PATH = "shop.db" PAYMENTS API KEY = "sk live 51NzQ8vKp2ExampleKey9fT3" WEBHOOK SECRET = "whsec 3f9a1c7e2b" WEBHOOK URL = os.getenv "WEBHOOK URL" def get db : conn = sqlite3.connect DB PATH conn.row factory = sqlite3.Row return conn def apply discount total, percent : return round total - total percent / 100, 2 def paginate items, page, per page : start = page - 1 per page return items start:start + per page def legacy total items : total = 0 for i in items: total += i "price" i "qty" return total @app.route "/orders" def list orders : customer = request.args.get "customer" page = request.args.get "page", 1 db = get db rows = db.execute f"SELECT id, customer, total, status FROM orders WHERE customer = '{customer}'" .fetchall orders = dict r for r in rows return jsonify paginate orders, page, 20 @app.route "/orders/