Vibe coding has made development more accessible. One person can now put together in an evening a prototype that used to need a small team. But it has also brought a new trap: it’s easy to mistake a working screen for a finished product.
An AI agent really can write code, explain errors, suggest an architecture and run tools. But code is only one stretch of a much bigger job. Before it come the product problem and the requirements; after it come testing, security, release and support.
The key shift looks like this: not “ask the model to build an app”, but build a process in which the agent takes limited steps while a human understands the goal, makes the decisions and checks the evidence.

Vibe coding isn’t the problem. The lack of boundaries is
Asking an AI to “build authentication” is a bit like telling a contractor to “paint something red”. They may well do it, but they don’t know what a good result is supposed to look like.
Is authentication an email and password, or sign-in through an external identity provider? Do you need account recovery, MFA, email confirmation, a limit on attempts, sign-out from all devices, a session audit? Where are the secrets stored? What happens on an error? What data does the user see?
If the answers aren’t written down, the agent will fill the gaps itself. You get a solution that looks convincing but isn’t necessarily right for the product. That’s where four familiar effects come from:
- The “holey sock”: the interface works, but inside there’s a vulnerability, a secret in the code or unvalidated data.
- The “hamster wheel”: each new fix cures one symptom and creates the next two.
- Destructive refactoring: a large chunk of the project gets rewritten for the sake of a small task.
- Loss of control: after a few iterations nobody can tell what changed and why.
So maturity isn’t about banning AI from writing code. It’s about not quietly handing it the product decisions and the risky ones.
An AI agent is an amplifier, not the person responsible for the product
An engineer is responsible for more than syntax. They keep in view the constraints, the cost of changes, security, operations and the consequences for users. An agent can propose a technically plausible option, but it doesn’t automatically know your company’s context and it isn’t liable for the damage.
This is particularly important for payments, personal data, medical and other critical scenarios. The higher the cost of a mistake, the less reason there is to treat the model’s answer as sufficient proof.
It helps to separate three things:
- Generation: the agent proposes text, code, a diagram or a possible solution.
- Decision: a human chooses an option, taking into account the product, the budget and the risks.
- Evidence: a test, a check, a log or an observable result confirms that the solution works.
AI is excellent at the first part and noticeably speeds up the second. The third is best built on reproducible mechanisms.
SDLC: code is the middle, not the whole life cycle
SDLC is the Software Development Life Cycle. It isn’t one mandatory scheme with a fixed number of stages: teams divide the cycle in different ways. But almost every model has a few recurring kinds of work:
- understand the problem, the users and the constraints;
- describe the requirements and the success criteria;
- design the system boundaries and contracts;
- implement the change;
- check functionality, quality and security;
- deliver the result to the user;
- monitor the product and support it.
The original lecture neatly lays this out as eight stages. It’s important to treat the number eight as a practical map, not a law of nature. In real work, requirements, security and testing don’t wait for coding to finish: they come back into the cycle after feedback and changes in the environment.
If you only ask the agent to write code, you’re using just part of the SDLC. The other parts don’t disappear. They simply turn into bugs found late, manual troubleshooting and unexpected costs.
Spec-driven: blueprint first, then implementation
A specification is a short agreement about the task. It’s worth writing down:
- what problem we’re solving and for whom;
- which scenario has to start working;
- what’s in the current version and what’s deliberately left out;
- constraints and architectural rules;
- the shape of the data and external contracts;
- the criteria for “done”;
- which files and subsystems must not be changed.
This isn’t bureaucracy. A spec reduces the number of decisions the agent is forced to guess. In a small project one PRD.md and a description of the specific slice are enough. In a more complex one you add ARCHITECTURE.md, CONVENTIONS.md, SECURITY.md and TESTING.md.
But documentation isn’t a magic source of truth. It has to match the code and the checks. An outdated spec is more dangerous than a short but current one.
An MVP doesn’t have to be perfect, but it has to be deliberate
An MVP tests a hypothesis. A production product serves real users. They have different criteria for “good enough”.
In an MVP you can skip complex analytics, ten integrations and an architecture “built to last a century”. What you can’t do is knowingly leave an obvious hole in data access, keep keys in the repository or accept arbitrary input where the business logic depends on it.
The right compromise goes like this: minimal functionality, yes; uncontrolled risk, no. Write down which MVP limitations are temporary, and who should revisit them and when.
Vertical slice: a small, complete route through the system
A vertical slice isn’t a layer, as in “first the whole database, then the whole backend”. It’s a user capability carried through all the necessary layers from start to finish.
For example, in a CRM for a recruitment agency the first slice might be this: a recruiter creates a candidate card, the form validates the fields, the API accepts the data, the service applies the business rules, the record appears in the database, and the interface shows success and reports errors properly.
That’s less than “build a CRM” but more than “draw a form”. A piece like this can be shown to a user to get feedback before the team builds a foundation for the wrong hypothesis.
FSM and lifecycle: don’t skip past the evidence
FSM stands for Finite State Machine. In practical terms it’s a set of states and the transitions allowed between them. In development it’s useful as process discipline: the next step doesn’t start until the current one is closed.
A practical feature lifecycle might look like this:
Context check: read the project rules, the current architecture and the constraints.Scope: define what’s in the task and what isn’t.Plan: name the steps and the expected changes.Components: list the affected parts: UI, API, data, tests.Implement: make the minimal changes.Verify: compare the result against the request.Test: run the automated and manual checks.Review: look for anything superfluous, regressions and broken boundaries.Document: update the documents if the behaviour has changed.Close: record the result and the open questions.

This isn’t waterfall. After testing you can go back to the requirements, and after feedback, back to the scope. What matters is that going back is explicit, not something that happens unnoticed inside an endless chat.
Definition of Done: “done” has to be verifiable
“Build a form” describes an intention, not finished work. A Definition of Done turns it into a set of observable conditions.
For an enquiry form it might be:
- the form works on desktop and mobile;
- required fields and the email format are validated on the server;
- invalid data doesn’t get past the business logic;
- success and failure are clear to the user;
- secrets don’t end up in the frontend;
- existing pages aren’t broken;
- the build and the tests pass;
- the documentation is updated if the contract has changed.
A good thing to tell the agent after implementation: “Check each item separately. For each one, give the evidence. Separately list what can’t be confirmed without a manual run or access to the environment.”
AI for ambiguity, tools for things that can be checked
An LLM gives a probabilistic answer. That doesn’t make it useless. On the contrary, flexibility is valuable where you need to interpret, compare options or find a non-obvious explanation.
But formatting, types, tests, the build, migrations and secret scanning are better checked with specialised tools. A linter doesn’t “think” about whether it likes the code; it applies fixed rules. TypeScript checks static types before the code runs, but it doesn’t automatically check data coming from the user. A test framework doesn’t replace a product scenario, but it does reproduce specific checks.

The principle is simple: anything that can be done by a reproducible script shouldn’t be left to the model alone. At the same time, a green build doesn’t prove that the product is easy to use, and the agent saying “done” doesn’t replace a smoke check.
Architectural boundaries: who is responsible for what
Architecture isn’t there for the sake of neat folders. It lowers the cost of change. If the UI draws the screen, checks permissions, talks to the database and holds business rules all at once, it’s hard for the agent to change one part without touching the rest.
At a basic level you can agree that:
- the UI shows state and accepts actions;
- the schema describes and validates the shape of the data;
- the service holds the business rules;
- the API coordinates the exchange;
- the data layer is responsible for storage.
MVC is one way to explain this kind of separation through Model, View and Controller. It’s a useful teaching model, but not a universal recipe for every modern application. A project may have other boundaries: feature modules, ports and adapters, server actions, or even a static site with no backend at all. What matters isn’t the name of the pattern but clear responsibilities and rules against unwanted dependencies.
DTO and Zod: a contract and a check are different things
A DTO, Data Transfer Object, describes the shape of the data that passes between parts of the system. It’s an agreement: which fields exist, which types and which constraints are expected.
Zod is a library for runtime schema validation in JavaScript/TypeScript. It’s useful because TypeScript types are checked before the code runs and don’t protect the application from an arbitrary HTTP request, a response from an external API or a bug in your own frontend.
So a DTO and a Zod schema aren’t the same thing: the first is a role, a data contract; the second is a specific tool for checking at runtime. At the system boundary, data has to be validated even when the frontend has already checked it.

Security is an ongoing practice, not a final tick box
It’s convenient to separate the security of development from the security of the running product, but it’s not worth turning them into two sealed-off rooms.
In development, what matters is secrets, repository access, dependencies, CI/CD and tool permissions. In the product, it’s authentication, authorisation, data handling, sessions, logs, rate limiting and incident response. The basic risk-based question goes like this: what are we protecting, against which scenarios, what damage is possible and which measures are proportionate to the project.
Minimum rules:
- don’t commit keys, passwords or tokens;
- don’t send real secrets or personal data to an AI without a clear need and a clear data-handling policy;
- limit access by the principle of least privilege;
- use proven libraries and standard protocols instead of home-made cryptography;
- don’t confuse
.envwith a proper secrets manager: the file helps you avoid putting a key in Git, but it has to be protected itself and isn’t necessarily the best store for production; - check authorisation boundaries and object-level access separately, rather than treating a successful sign-in as proof of the right to do everything.
OAuth is a protocol for delegated authorisation, not a universal synonym for logging in. The specific flow depends on the identity provider and the application. MFA is a broader concept than 2FA: two-factor authentication is a special case of multi-factor.
Anyone working with AI needs to understand the risk map
You don’t have to write every line by hand. But you do need to be able to tell the client from the server, the API from the database, authentication from authorisation, a static type check from runtime validation, a local run from a production deploy.
This isn’t a requirement to become a senior engineer. It’s the minimum literacy for whoever commissions the work and runs the process. Otherwise the agent will make decisions for you in places where you can’t judge the consequences.
A working request template
Instead of “build a CRM”, give the agent context and boundaries:
We’re building a CRM for a small recruitment agency. Right now we’re implementing one vertical slice: creating a candidate card. The goal is for a recruiter to save a name, an email and the current status and get a clear result.
Use
PRD.md,ARCHITECTURE.mdandCONVENTIONS.md. Don’t change authentication, payments or the existing API outside this scenario. First do a context check, list the scope and propose a plan with files and checks. Don’t write any code yet.Once it’s agreed, implement only the plan. Then check the Definition of Done, run the available tests and state separately: what has been proven, what has been checked indirectly and what needs a manual check.
A prompt like this doesn’t make the model infallible. It cuts down on guesswork and creates points where a human can stop a wrong decision.
The bottom line: mature work with AI means managing the process
Moving from vibe coding to engineering doesn’t mean giving up speed. It means pointing that speed along a route that can be checked.
A good process looks like this: understand the product, write down the requirements, define the boundaries, pick a small slice, take it through the lifecycle, check the result with deterministic tools, assess the risk, update the documentation, and only then call the task closed.
In this process AI becomes neither a magic button nor “a junior developer who’s allowed to do anything”, but a powerful participant in an engineering system. The clearer the context and the criteria, the more value you get from what it generates, and the lower the cost of the mistakes that will inevitably happen.
Lecture by: Igor Bergand





