Vibe coding é armadilha
O prompt é o pedido que você escreve pra IA. "Faz um app de agendamento". Em dez minutos tem tela, botão e uma lista. Aí dois clientes marcam o mesmo horário, ou o botão diz que salvou e o banco está vazio. Banco é onde a agenda fica gravada. Alguém pergunta por que quebrou. Se a resposta não sai, o problema não foi a velocidade. Foi aceitar código que você não consegue explicar.
Prompt curto, app de agendamento em dez minutos. Dois horários iguais entram, ou o catch devolve ok e a linha não existe. Se você não explica o código, a velocidade não era o problema.
O problema não é a IA
A IA acelera quem já sabe o que tem que existir. Para quem não sabe, ela esconde a parte que precisava ser aprendida. A trava é uma regra que o banco aplica sozinho. Ela falha em três lugares.
A IA acelera quem já sabe a invariante. Sem ela, o modelo esconde o que você precisava aprender. Três furos: constraint, erro engolido, RLS.
Atalho
Pular a parte de entender parece ganho. No agendamento, entender é uma frase: o mesmo profissional não pode ter dois horários iguais. Insert é o pedido de gravar uma linha. Sem a trava no banco, os dois inserts passam. A tela mostra os dois. O código "funcionou".
A invariante é uma: o mesmo profissional não pode ter dois inícios iguais. Sem unique (profissional_id, inicio), os dois inserts passam. Não há erro. Há duplicata, e você procura bug na tela.
| profissional | início |
|---|---|
| o mesmo | 15:00 |
| o mesmo | 15:00 |
alter table public.agendamentos
add constraint um_horario_por_profissional
unique (profissional_id, inicio);Com a trava, o segundo pedido de gravar é recusado. Aí existe um erro pra ler. Sem a trava, os dois entram. Você procura o problema na tela, e a tela está certa.
Com a constraint, o segundo insert falha. Sem ela não há exception. Há duas linhas.
Bug
Este é o tipo de função que a pressa aceita. Catch é o trecho que pega a falha. Neste, ele pega e responde que deu certo. O botão fica verde. A linha não foi salva.
O catch devolve sucesso. A UI fica verde. A linha não foi salva.
| tela | banco |
|---|---|
| Salvo | nada |
async function criarAgendamento(dados) {
try {
await salvar(dados);
return { ok: true };
} catch (erro) {
return { ok: true };
}
}Você não sabe onde procurar porque o aviso sumiu. O lugar é o catch. Ele tem que devolver o erro, ou mostrar a mensagem, não fingir que deu certo.
O catch apagou o stack. Devolva o erro, ou a mensagem. Não retorne ok.
Segurança
RLS é a regra de quem pode ler cada linha. Sem ela, a chave pública do site, a que vai no navegador, lê a agenda de todo mundo. A chave secreta é a de administrador. Se ela estiver no JavaScript da página, quem abre o site copia a chave.
Tabela sem RLS, lida pela anon key no navegador, devolve a agenda inteira. Service role no JavaScript ignora qualquer policy.
alter table public.agendamentos enable row level security;
create policy "cada um vê o seu"
on public.agendamentos
for select
to authenticated
using ((select auth.uid()) = user_id);Como usar direito
IA é dupla, não muleta. Quem responde pelo código continua sendo você. Estas cinco regras cabem num arquivo. Cada uma existe para você não aceitar o próximo arquivo no escuro.
Prompt é o pedido. Commit é um ponto salvo do projeto: se a próxima mudança quebrar, você volta nele.
- Uma tarefa por prompt. "Cria a trava de horário único" é uma tarefa. "Faz o app inteiro" mistura tela, banco e regra, e você não revisa nada.
- Peça para explicar o código antes de aceitar. Se a explicação não diz o que muda no banco e qual erro ela engole, não entra.
- Leia a doc oficial da ferramenta que o código usa. Texto de blog e resposta de chat atrasam. A página da RLS, do framework ou da API é a fonte.
- Commit antes de pedir a próxima mudança. Se a alteração quebrar, você volta num comando, em vez de caçar o que mudou no meio de dez arquivos.
- Teste o caso que dói. Dois horários iguais. Usuário sem id. Salvamento que falha. Se só o caminho feliz foi clicado, o app não foi testado.
Um prompt de revisão que dá para colar em cima de qualquer função gerada:
Explica essa função como revisão de código.
O que ela grava no banco?
Qual erro ela esconde?
O que acontece se o id do usuário vier vazio?
Não mude o arquivo. Só responda.Júnior vai sumir?
Não. Some a tarefa de digitar o óbvio sem saber o que ele faz. Continua o trabalho de ler o erro, achar a linha e decidir se a regra do negócio está no banco.
Quem está começando e usa a IA para perguntar "por que esse catch devolve ok?" aprende mais rápido do que quem datilografava a mesma função em 2019. Quem cola o app inteiro e não sabe responder quando quebra fica travado. Isso não é só júnior. Sênior nesse modo trava do mesmo jeito, só que o estrago é maior.
Se você discorda, o argumento tem que dizer qual trabalho some e quem passa a fazer essa parte. "A IA faz tudo" não responde quem olha o banco quando dois horários iguais entram.
