Over the course of a corporate software engineering internship, I designed and built two successive AI-agent platforms almost entirely through AI-assisted development — what people now call “vibe coding.” The first was a production-grade recruitment automation system: a 12-step AI agent pipeline spanning five services, with 38/38 end-to-end tests passing. The second, built right after, was a client invoicing system that replaced a manual Canva-and-Excel process with a single source of truth for every calculation, tracing each business rule to a real invoice or spreadsheet row on file.
But the real lesson wasn't that AI can write code. It was that vibe coding without a methodology produces fragile software, and that a disciplined process is what turns fast prototyping into something you can actually defend and ship. This article shares the methodology I developed, and shows how it played out identically on two very different systems.
The Context
Platform One — Recruitment Automation
At the HR consultancy where I interned, I built an end-to-end automated recruitment pipeline: an AI agent that takes a hiring need and drives it all the way from job-description generation, through sourcing, CV parsing, candidate scoring, interview scheduling, and AI-generated interview grids, to the final offer, invoicing, and onboarding follow-up.
The stack was deliberately layered:
- Frontend — Next.js / React (recruiter portal, real-time tracking board)
- Backend — Node.js / Express + Socket.IO (APIs, business rules, real-time events)
- Orchestration — n8n in Docker (multi-step workflows)
- AI service — Python / FastAPI (parsing, scoring, synthesis)
- Data — MongoDB (8 collections), the whole thing on Docker Compose
Platform Two — Client Invoicing System
The second platform automates the company's client invoicing. Before it, every invoice was composed in Canva by duplicating the previous one, exported to PDF, then re-typed by hand into an Excel tracking sheet — two manual entries, no link between the document and the record, and errors that a formal audit later confirmed (wrong VAT, duplicate invoice numbers, inconsistent formats). The platform inverts that: one entry produces both the PDF invoice and the full sales register. Every rule in the system — 34 numbered rules in total — is traced to a real invoice or spreadsheet row in a written specification; nothing is implemented that isn't grounded in an observed document.
- Application — Next.js / React, server actions only, no separate backend service
- Data — SQLite via Prisma, chosen deliberately for a single-writer, low-volume system, with a documented migration path to PostgreSQL if volume grows
- Documents — a headless Chromium renderer turning an HTML template into the archived PDF; a spreadsheet library generating the sales register and exports
- Money — every amount stored as an integer number of cents, every quantity as an integer number of thousandths of a unit — never a floating-point number
I wrote most of both platforms with AI coding assistants. And that is exactly why the how mattered more than the what, on both of them.
The Uncomfortable Truth About Vibe Coding
Vibe coding is intoxicating. You describe what you want, code appears, it mostly runs. But “mostly runs” is a trap. Left unchecked, AI-generated code accumulates hidden bugs, silent inconsistencies, security gaps, and architectural drift — faster than you can read it.
The turning point for me was realizing that the AI is not an oracle. It is a very fast, very confident junior developer whose output must always be verified. Once I internalized that, I stopped “vibing” and started engineering. Here is the methodology that came out of it.
A Methodology for Disciplined Vibe Coding
1. Plan before you prompt
Write a short spec first: inputs, outputs, edge cases. Ask the AI to produce a plan and confirm it before generating code. Never let 300 lines grow from a vague one-liner.
2. Evaluate the stack — advantages and disadvantages
Before committing to any framework, weigh it explicitly: what does it do well? Where does it struggle? How mature is the ecosystem? Popular does not mean right. Choose on trade-offs, not on hype.
3. Know the limitations
Every tool — and the AI itself — has hard limits: rate limits, size caps, things a framework simply cannot do. Surface them early by asking “what can't this do?” so you don't build halfway before hitting a wall.
4. Map the risks
Identify what could go wrong before it does: security holes, secrets in code, weak auth, data loss, scaling failures, vendor lock-in, fragile dependencies. The AI won't volunteer these — ask it to list them explicitly.
5. Compare approaches
For any non-trivial feature, ask for two or three solutions with trade-offs (simple vs. scalable, fast to build vs. maintainable). Decide deliberately instead of accepting the first answer.
6. Work in small, testable increments
One feature at a time. Test it, commit it, move on. Small diffs are reviewable; large AI dumps are where bugs hide.
7. Use version control religiously
Commit after every working step. Branch for experiments. When the AI breaks something, roll back instead of debugging a mess.
8. Read the code — don't just accept it
Skim every block. If you don't understand a line, ask for an explanation. Accepting code you can't read is the number-one source of untraceable bugs.
9. Keep a tight error feedback loop
Paste full error messages and stack traces back to the AI — not summaries — along with the actual failing input and the expected output.
10. Automate the boring checks
Linting, formatting, and tests alongside features catch whole classes of issues before you ever run the code.
11. Graduate to production, deliberately
A platform that passes every test on a laptop is not automatically a platform you can hand to real users. Treat “it works” and “it's production-ready” as two different bars, and cross the second one on purpose, not by accident: real authentication in front of every endpoint, secrets pulled from an environment or a vault rather than hard-coded, backups you have actually restored once rather than just taken, monitoring and alerting so a failure is noticed before a client reports it, a rollback path for the day a deploy goes wrong, and a documented handover so someone other than you can operate the system. Both platforms were deliberately scoped as prototypes first — their own internal documentation says so explicitly — and that same discipline is what now makes the move to production a planned step rather than a scramble: the gaps were named on purpose, so they can be closed on purpose, one at a time, instead of being discovered by an incident.
Where Discipline Paid Off
Recruitment Platform
Two design decisions show what the methodology bought me:
- Deterministic scoring, never LLM-decided. Candidate scoring uses fixed, explainable weights (required skills, experience, location, and so on) with clear thresholds. In HR, algorithmic discrimination is a real legal risk — so the scoring is auditable criterion by criterion, not a black box. That was a risk-mapping decision, not a coding one.
- A dual-engine, degraded-mode design. The system uses a real LLM when available and falls back to a deterministic rules engine otherwise, so it runs fully offline. If the orchestrator is unreachable, an internal fallback takes over. That came straight from asking “what could break here?”
Full traceability (every status change is journaled), anti-double-booking calendar locks, and a retry mechanism that resumes without duplicating data rounded it out.
Invoicing Platform
The same instincts showed up again, on a completely different domain:
-
Money as integers, never floats. A real invoice in the source data has
an HT amount of 72,615.60; computing its VAT in JavaScript as
72615.60 * 0.20silently returns14523.120000000003, not14523.12. Knowing that limitation up front — not discovering it in production — is why every amount in the system is stored as an integer number of cents. A single calculation module is called by the screen, the PDF, and the register, so the historical bug it replaces (wrong VAT, because amounts were typed by hand and never computed) can't be reintroduced by a second copy of the formula drifting out of sync. - Uniqueness guaranteed by the database, not by application code. Invoice numbers are assigned by an atomic counter (client code + year) inside a database transaction, so two simultaneous emissions can never collide — duplicates become impossible by construction, the same guarantee the calendar lock gives the recruitment platform.
- Documents sealed, not regenerated. Once an invoice is issued, its PDF is rendered exactly once, archived, and fingerprinted (SHA-256); every later read serves that archived file, never a fresh render. Correcting a mistake means issuing a brand-new invoice and marking the old one superseded — never editing or deleting it. That is an audit trail decision, made by mapping the risk (“what if a document already in a client's hands silently changes?”) before writing a line of code.
The same discipline surfaces different guarantees depending on the domain: explainable scoring where the risk is algorithmic bias, immutable sealed documents where the risk is fiscal non-compliance. Neither came from vibing; both came from mapping the risk before writing the feature.
The core loop: spec → evaluate stack → assess limits & risks → compare approaches → small change → review → test → commit → repeat → to production.
Vibe coding gives you speed. Methodology gives you software you can defend. The two together are a genuine superpower.