Vibe Coding - ¿Realmente es tan malo?
Vibe Coding: cuando el código "funciona"... pero no sabemos por qué
En los últimos años apareció un fenómeno bastante común: el vibe coder. Ese desarrollador que programa por intuición, copia snippets, prueba cosas hasta que más o menos anda… y luego pasa a lo siguiente.
El problema no es que el código funcione hoy. El problema es cuando alguien tiene que mantenerlo mañana (a veces ese alguien sos vos mismo).
En este artículo vamos a ver errores clásicos del vibe coding, por qué son peligrosos y cómo corregirlos.
Ante todo, no estoy en contra del vibe coding, pero si considero que al realizar este tipo de prácticas se debería ser más responsables y detallados.
1. Credenciales expuestas en el código
Error típico
javascriptconst db = connectDB({ host: "localhost", user: "admin", password: "123456", database: "app_prod" });
Esto aparece muchísimo en repositorios de GitHub.
Problemas:
- credenciales filtradas
- ataques automatizados
- bases de datos comprometidas
- imposible rotar credenciales fácilmente
Corrección
Usar variables de entorno.
javascriptconst db = connectDB({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME });
Y un .env
DB_HOST=localhost
DB_USER=admin
DB_PASSWORD=strongpassword
DB_NAME=app_prod
Regla simple:
Si hay un secreto en tu código, ya lo filtraste.
2. SQL Injection por concatenación de strings
Error típico
javascriptapp.get("/user", (req, res) => { const query = "SELECT * FROM users WHERE id = " + req.query.id; db.query(query, (err, result) => { res.send(result); }); });
Un atacante puede mandar:
?id=1 OR 1=1
Y listo: acceso a toda la base.
Corrección
Usar queries parametrizadas.
javascriptapp.get("/user", (req, res) => { const query = "SELECT * FROM users WHERE id = ?"; db.query(query, [req.query.id], (err, result) => { res.send(result); }); });
Esto evita que el input se ejecute como SQL.
3. El archivo monstruo de 2000 líneas
Error típico
app.js
Contiene:
- rutas
- lógica de negocio
- conexión a base
- validaciones
- middlewares
- helpers
- utilidades
Todo en el mismo archivo.
Resultado: infierno de mantenimiento.
Corrección
Separar responsabilidades.
Ejemplo de estructura simple:
src/
├ controllers/
│ └ userController.js
├ services/
│ └ userService.js
├ routes/
│ └ userRoutes.js
├ models/
│ └ userModel.js
└ app.js
Regla básica:
Si tu archivo requiere mucho scroll, ya es una red flag.
4. Código duplicado por todos lados
Error típico
javascriptfunction calculatePrice(price){ return price * 1.21; } function calculateCart(price){ return price * 1.21; } function calculateTotal(price){ return price * 1.21; }
Tres funciones iguales.
Cuando cambie el impuesto, hay que modificar todo el código.
Corrección
Centralizar lógica.
javascriptfunction applyTax(price){ const TAX = 1.21; return price * TAX; }
Luego reutilizar.
javascriptconst total = applyTax(product.price);
Principio clave:
DRY — Don't Repeat Yourself
5. Manejo de errores inexistente
Error típico
javascriptapp.get("/users", async (req,res)=>{ const users = await db.getUsers(); res.json(users); });
Si la base falla → la app crashea.
Corrección
Manejo de errores explícito.
javascriptapp.get("/users", async (req,res)=>{ try{ const users = await db.getUsers(); res.json(users); }catch(error){ console.error(error); res.status(500).json({error:"Internal server error"}); } });
Esto evita:
- crashes
- fugas de información
- errores silenciosos
6. Código espagueti (dependencias cruzadas)
Error típico
userController → orderService → paymentService → userController
Dependencias circulares.
Después nadie entiende qué se ejecuta primero.
Corrección
Arquitectura clara.
Patrón simple:
Routes
↓
Controllers
↓
Services
↓
Repositories / Models
Cada capa tiene una responsabilidad clara.
7. No hay validación de inputs
Error típico
javascriptapp.post("/register",(req,res)=>{ createUser(req.body); });
Si alguien manda:
json{ "email": "<script>alert(1)</script>" }
Puede terminar en:
- XSS
- datos corruptos
- errores inesperados
Corrección
Validar datos.
Ejemplo simple:
javascriptif(!req.body.email){ return res.status(400).send("Email requerido"); }
O usar librerías de validación.
8. Sin tests
El vibe coder prueba así:
funciona en mi máquina
Pero cuando el código crece:
- aparecen regresiones
- bugs inesperados
- deploys peligrosos
Corrección
Agregar tests mínimos.
javascripttest("applyTax adds 21%", ()=>{ expect(applyTax(100)).toBe(121); });
No hace falta obsesionarse con cobertura.
Pero algo de testing cambia todo.
Conclusión
El vibe coding no es el enemigo.
De hecho es muy útil para prototipar rápido.
El problema aparece cuando el prototipo se convierte en producción sin disciplina técnica.
La diferencia entre un proyecto amateur y uno profesional suele ser:
- estructura
- seguridad
- mantenibilidad
- escalabilidad
Y la regla de oro:
Si dentro de seis meses no entendés tu propio código, el problema no es la memoria. Es la arquitectura.