Vibecoders breaking production

Thirteen checks before you push anything to production. The same ones I run, ordered by how much it hurts if you skip them.

Contents

You have the app finished and your finger over the ship button. This is the list I go through before hitting it, ordered by how much it hurts if you skip it.

This is not a list of best practices for some day: it is twelve concrete checks, each one with what to look at and how to look at it. At the end there is a prompt you hand to your agent and it comes back with the whole review done on your code.

1. Test the critical flows end to end

Un flujo crítico, de punta a punta Entra Registro o login Hace lo suyo Lo que vende tu app Paga Si hay dinero de por medio Vuelve Y sigue estando todo Si esta fila entera no pasa en una máquina que no es la tuya, no tienes una aplicación: tienes una demo que funciona en tu portátil.
The whole path, from the first screen to the last click

A critical flow is the one that, if it breaks, your app is worth nothing: getting in, doing what it promises and, if you charge, paying. Testing it end to end means walking the whole thing as somebody arriving from scratch would, not checking that each piece works on its own.

And do it on a computer that is not yours, or at least in an incognito window and with another account. Almost everything that falls over on launch day worked perfectly on the machine of whoever wrote it, because there the session was already open, the cache was warm and the data was the usual data.

2. Test login, signup and password recovery

Las cuatro puertas de una cuenta Registro con un correo que nunca ha entrado Login, cierre de sesión y volver a entrar Recuperar contraseña, con el correo abierto El enlace de recuperación caduca y se gasta una vez Probarlo solo con tu cuenta, que ya existe Dar por bueno el correo porque «ya se enviará» La recuperación de contraseña es la que nadie prueba y la que más se rompe, porque depende de un correo que sale de tu servidor y llega a otro sitio.
The four doors into an account, and the one nobody tests

There are four paths, not one: signing up with an email that has never been used, logging in, logging out and logging back in, and recovering the password. The fourth is the one that always breaks, because it is the only one that depends on an email leaving your server and actually arriving somewhere else.

Check the boring parts too: that the recovery link expires, that it can only be used once, and that logging out invalidates the session on the server and not just clears a cookie in the browser. And try signing up with an email that already exists: that has to say something clear, not blow up.

3. Review roles and permissions

¿QUIÉN LLAMA? Sin sesión Con sesión Ruta pública Ruta privada ¿QUÉ PIDE? Entra Como debe Entra Como debe Al login Y vuelve a donde iba Según su rol Comprobado en servidor LO FÁCIL LO QUE SE OLVIDA Prueba cada casilla con una cuenta de cada rol. Y la que se salta todo el mundo: un usuario normal pidiendo a mano la URL de administración.
Every cell, with an account of each kind

If your app has more than one type of user, you have a table to test: every route by every role. And there is one cell almost everybody skips, which is a normal user typing the admin panel address by hand. Not showing them the link does not mean they cannot get in.

What matters is where it is checked. Hiding a button in the browser is not a permission: it is make-up. The permission is checked on the server, on every request, and if it is not there, it does not exist. Test what happens when you take a role away from somebody while their session is open too.

4. Check the environment variables

Tu portátil Donde todo funciona .env con todo puesto Y variables que ya ni recuerdas Base de datos de juguete Con tus datos de siempre Claves de prueba sk_test, sandbox, localhost Producción Donde se rompe Cada variable, a mano Y una que falta lo tira todo Base de datos de verdad Vacía el primer día Claves de cobro real sk_live, dominio real Que la aplicación no arranque si le falta una variable obligatoria. Enterarte por un 500 a las tres de la mañana sale mucho más caro.
What is on your laptop and what is in production

Your laptop has a .env that has been filling up for weeks and that you no longer remember in full. Production has none of that until you put it there by hand, variable by variable, and one missing is enough for the app to start halfway and fail in the strangest place.

Make it refuse to start if a required one is missing. It is one check at the beginning of the process that stops the deploy with a clear message, and it saves you finding out through a 500 at three in the morning. Check that the keys are the real ones and not the test ones too: sk_test and sk_live look very alike at eleven at night.

5. Not one key published, anywhere

Dónde acaba una clave sin que nadie la escriba En el gestor de variables de tu hosting .env en .gitignore y un .env.example vacío En el bundle: si el navegador la ve, es pública En los registros del despliegue En una captura del panel que subes a X Lo que entra en el historial de Git se queda en el historial de Git. Si se publicó, no se borra: se rota.
Five places a secret ends up without anybody writing it there

Keys live in your hosting's variable manager and nowhere else. The .env in .gitignore, a .env.example with the names and none of the values, and that is it. Everybody knows this and it is still how they get in, because the problem is not the key you write: it is the one that slips out without you writing it.

The three places it slips out: in whatever reaches the browser, because any variable that travels to the client is public by definition; in the deployment logs, which print whole environments; and in the screenshot of the dashboard you post on social. And if it was published, it does not get deleted: it gets rotated. What enters Git history stays in Git history.

6. Test payments and webhooks for real

El pago no acaba en la pasarela Paga Con tarjeta de prueba Webhook La pasarela te avisa Verificas La firma, siempre Das acceso Una vez, no tres Prueba también el pago que falla y el que llega dos veces: la pasarela reintenta, y sin idempotencia le cobras o le das acceso doble.
The payment does not end when the user sees “thank you”

The charge does not end at the payment provider: it ends when your server receives the notification, checks it comes from who it says, and gives the user access. That notification is the webhook, and it is the piece nobody tests because locally it does not arrive on its own. You have to forward it with the provider's tool, and you have to verify the signature every time.

Test the three cases, not just the good one: the payment that works, the card that gets declined and the notification that arrives twice. Providers retry when you do not answer fast, so if your code is not idempotent you will hand out two months of access, or charge twice. And make at least one real one-euro charge before opening.

7. Test the errors, the loading and the empty state

Las cuatro caras de la misma pantalla 01 Cargando Con la red lenta, no instantánea 02 Con datos La única que sueles mirar 03 Vacía El primer día de cada usuario 04 Con error Y con forma de volver a intentarlo La pantalla vacía es la primera que ve todo el mundo y la última que dibuja cualquiera. Si dice «no hay nada», dile también qué hacer.
The four faces of the same screen

Every screen that asks for data has four states and you have only looked at one. There is the one loading, the one with data, the one with none and the one that failed. The one with data is the only one you see while developing, because your database has been full for months and your network is instant.

The empty one is the most important of all: it is the first one every new user sees. If it says “nothing here”, you are wasting the best moment to tell them what to do. And the error has to offer a way out —retry, go back— rather than leaving somebody staring at a blank screen. Test the loading with the network throttled from the browser.

8. Check mobile properly

Por dónde te van a entrar Móvil ~70% Escritorio ~25% Tableta ~5% Reparto típico de una web que llega desde redes. Tú la has construido en un portátil de 15 pulgadas y ellos la van a abrir en 390 píxeles de ancho.
Where they will come in, and where you built it

You built it on a laptop and they are going to open it on 390 pixels of width, especially if they arrive from social. Narrowing the browser window is not enough: there are things you only see on a real phone, like the keyboard covering the field you are filling in, or the area you can actually hit with your thumb.

Look at what always breaks: wide tables that put a horizontal bar across the whole page, buttons stuck to the bottom edge where the browser bar lives, text that overflows and modals you cannot close. And open it in Safari on an iPhone, which is still the one that behaves differently.

9. Remove debug mode, mocks and test data

El cementerio de toda app hecha deprisa console.log con el objeto del usuario entero La ruta /test que montaste para salir del paso Datos falsos: «Juan Pérez», lorem ipsum, precios a 1€ El botón que salta el login «solo mientras desarrollo» Banderas de prueba encendidas en producción Búscalo con el buscador del editor, no de memoria: TODO, FIXME, test, mock, dummy, localhost, console.log.
The graveyard of every app built in a hurry

Every app built in a hurry has its graveyard: the /test route you put together to get by, the “John Smith” user with a one-euro price, the console.log that prints the whole user object and, worst of all, the flag that skips the login “just while I am developing”.

Do not look for it from memory, look for it with the editor's search: TODO, FIXME, test, mock, dummy, localhost, console.log. And turn debug mode off in production, because a detailed error on screen tells whoever is watching how your app is put together inside.

10. Turn on logs and error alerting

Enterarte tú antes que el usuario Se rompe A alguien, a las 3AM Se captura Con la traza y el usuario Te avisa Correo o Slack Lo arreglas Antes de que escriba Sin esto tu sistema de avisos es que un usuario se moleste en escribirte. La mayoría no escribe: se va.
Finding out before your user does

If you have nothing set up, your alerting system is a user bothering to write to you. And most do not write: they close the tab and do not come back. You need two things, and both have free plans: logs you can read and a tool that captures errors with their stack trace and tells you.

Make the alert land where you actually look, be it email or Slack. And when you store the error, store which user it was and what they were doing with it, but without putting passwords or tokens inside, which is exactly point five of this list repeating itself through the back door.

11. Set up backups

Una copia que no has restaurado no es una copia Automática y diaria, sin que nadie se acuerde Guardada en otra cuenta, no junto a la base de datos Restaurada una vez de verdad, cronómetro en mano «El hosting ya hace copias», sin haberlo mirado En el mismo servidor que se puede caer Las dos preguntas que hay que saber responder antes de necesitarlo: cuánto dato puedo perder y cuánto tardo en volver.
A backup you have never restored is not a backup

Automatic, daily and stored somewhere else, not next to the database that can go down with it. Almost every managed service ships with them, but “shipping with them” and “working” are different things until you check it yourself.

And here is what almost nobody does: restore one, just once, with a stopwatch in your hand. Until you do, you do not know the two things you have to know before you need it, which are how much data you could lose and how long it takes you to be back on your feet.

12. Have the rollback ready

SIN PLAN DE VUELTA Despliegas y algo se rompe Buscas el fallo con gente dentro Parcheas a ciegas y con prisa Cada minuto es un usuario menos CON PLAN DE VUELTA Despliegas y algo se rompe Vuelves a la versión anterior Buscas el fallo con calma Los usuarios no se enteran Volver atrás tiene que ser un botón que ya sabes dónde está, no algo que buscas por primera vez mientras arde. Pruébalo antes de necesitarlo.
The same failure, with a plan and without one

You are going to deploy something that breaks the app. That is not pessimism, it is statistics. The difference between a two-minute scare and a hellish afternoon is whether going back to the previous version is a button you already know where to find, or something you look for the first time while people are complaining.

Find out today how to roll back on your hosting and try it once with the app running. And if you have database migrations, check the previous version still works with the new schema, because the code goes back in one click and the data does not.

13. Let the machine check it: pre-commit hooks

Antes de cada commit Segundos, o lo desactivan Formato y linter Solo sobre lo que has tocado Tipos tsc --noEmit, si el proyecto es grande Buscar secretos sk_, AKIA, .env que se te ha colado Antes de subir, o en CI Minutos, y ahí sí Los tests Los que tardan de verdad El build entero Que compile como en producción Flujos de punta a punta Lo del punto 1 La regla que decide si esto sobrevive: si el pre-commit tarda más de unos segundos, el equipo aprende --no-verify y ya no te protege de nada.
What gets checked in seconds and what gets checked in minutes

Everything above depends on you remembering, and you are not going to remember. A pre-commit hook is a script Git runs before letting you save the change, and if it fails, there is no commit. It is the difference between a list you read and a list that enforces itself.

In Node projects the usual choice is Husky, which in version 9 installs with one command and leaves the hooks in a .husky folder that does get committed, so everyone who clones the repo gets them. It is almost always paired with lint-staged, which is what makes the checks run only on the files you touched rather than on the whole project.

The alternative gaining the most ground is lefthook: it is a Go binary, it does not depend on Node, it is configured in a YAML file and it runs tasks in parallel, which shows on big projects. It needs a lefthook install the first time, because it does not hook itself up when you install dependencies. And if your project mixes languages, pre-commit is still the one that handles that best.

What matters is not which one you pick, it is what you put inside. The pre-commit gets whatever takes seconds: formatting, the linter on what you touched, types if it is worth it, and a search for keys and console.log. The full test suite and the build go in the pre-push or in CI. If the pre-commit takes half a minute, your team discovers --no-verify within two days and it stops protecting you from anything.

Do not go through it from memory: hand it to your agent

Twelve points are not something you remember at eleven at night with the itch to publish. That is why the button below is here: it is this whole list turned into a job your agent understands.

You paste it into Claude Code, into Cursor or whichever one you use, and it hands you back a report point by point, with the file and the line, ordered from most to least serious and without touching anything until you say so. And whatever comes back red, you already know where to look, because it is above.