O dia em que um QR Code substituiu 20 minutos de fila
Lançar o Parusya para usuários reais me ensinou mais sobre engenharia de software do que qualquer projeto acadêmico. O código foi a parte fácil.
Tem um momento no desenvolvimento de qualquer sistema que separa teoria de realidade.
Não é quando você faz o primeiro commit. Não é quando o deploy funciona. Não é nem quando os testes passam.
É quando uma pessoa real, que não sabe nada sobre o seu código, usa o sistema pela primeira vez — e você está do lado de fora, sem poder ajudar, só observando.
Esse momento chegou numa sexta-feira à noite, num estacionamento.
Os meses antes
O Parusya levou meses para sair do papel. Não meses de desenvolvimento contínuo — meses de decisões, erros, refatorações, e aquela sensação constante de que "ainda não está pronto".
O backend em Java com Spring Boot foi tomando forma: autenticação JWT com criptografia RSA assimétrica, três perfis de usuário em tabelas separadas, geração de QR Code via ZXing, constraints de unicidade no banco para impedir check-in duplicado. O frontend em React com leitor de câmera via html5-qrcode. Deploy no Railway, Vercel, Cloudflare.
Em algum momento, a lista de coisas para fazer parou de diminuir — e eu percebi que estava procrastinando o lançamento. O sistema funcionava. Eu é que não estava pronto para ele falhar na frente de todo mundo.
Mas tinha uma semana marcada. E ela chegou.
O momento
300 jovens chegando. Equipe de recepcionistas com celular na mão, logados como EventStaff. Eu do lado de fora fingindo estar tranquilo.
O primeiro participante chegou.
Abriu o celular. Mostrou o QR Code. A recepcionista apontou a câmera.
Bip.
Nome apareceu na tela. Check-in registrado. Timestamp gravado no banco com participant_id, event_id, staff_id e o horário exato de chegada.
Eu quase chorei no estacionamento. Não porque foi tecnicamente impressionante — mas porque eu vi o momento exato em que um problema deixou de existir.
Nenhuma fila. Nenhum nome digitado errado. Nenhuma planilha travando. Nenhum estresse.
Só um QR Code e meio segundo.
O código é a parte fácil
Mas sabe o que ninguém te conta sobre lançar um sistema para usuários reais?
O código é a parte fácil.
O que realmente testou o sistema não foi a complexidade técnica. Foi o encontro com o comportamento humano — e com todas as situações que eu não havia previsto sentado na frente do computador.
Convencer a equipe a mudar a rotina. A planilha existia há anos. As pessoas sabiam usar. Mudar rotina gera resistência, mesmo quando a nova opção é melhor. Foi necessário treinar, explicar, mostrar em tempo real que era mais simples — não apenas dizer que era.
O cadastro precisava ser simples de verdade. Eu sabia que o sistema era fácil de usar. Mas "fácil para quem construiu" e "fácil para uma pessoa de 60 anos que nunca criou conta em nada" são coisas completamente diferentes. A tela de cadastro foi redesenhada três vezes até chegar num fluxo que qualquer pessoa completava sem pedir ajuda.
3G fraco no estacionamento. Esse foi o teste de estresse que eu não havia simulado. A câmera abria, lia o QR Code, enviava o request — mas em conexões lentas o feedback demorava 2, 3 segundos. Para a recepcionista, parecia que havia travado. A solução foi melhorar o feedback visual: um spinner claro, mensagem de "processando", e estado desabilitado no botão enquanto aguardava resposta.
O participante sem celular. Acontece. A pessoa esqueceu o celular no carro, ou a bateria morreu. Para esses casos, mantivemos um fluxo alternativo: a recepcionista pode fazer check-in pelo nome diretamente no sistema. Não é o fluxo principal, mas precisa existir.
Produto não é só o que você constrói. É o que acontece quando pessoas reais usam o que você construiu.
A decisão técnica que salvou a noite
Antes do lançamento, eu tinha corrigido um bug que poderia ter comprometido tudo: check-in duplicado.
O cenário era simples: uma recepcionista escaneia o QR Code de alguém que já entrou. Talvez por acidente, talvez porque a pessoa voltou para buscar algo. Sem proteção, o sistema registraria duas presenças para a mesma pessoa no mesmo evento.
O relatório final diria 320 presentes. A realidade seria 300.
Apresentar dados errados para a coordenação — dados que influenciam decisões sobre o grupo — destruiria a confiança no sistema inteiro.
A solução foi dupla. Primeiro, uma verificação na camada de aplicação:
if (checkInRepository.existsByParticipantIdAndEventId(participantId, eventId)) {
throw new BusinessException(ErrorCode.DUPLICATE_CHECKIN);
}
Mas só isso não era suficiente. Em condições de alta concorrência — 100 pessoas chegando ao mesmo tempo, múltiplos celulares escaneando simultaneamente — dois requests poderiam passar pela verificação de aplicação ao mesmo tempo, antes que qualquer um deles tivesse gravado no banco.
A segunda camada foi uma constraint diretamente no banco de dados:
@Table(
name = "check_ins",
uniqueConstraints = {
@UniqueConstraint(
columnNames = {"participant_id", "event_id"}
)
}
)
Mesmo que o frontend falhe. Mesmo que dois celulares escaneiem o mesmo QR Code no mesmo segundo. O banco rejeita. O dado está protegido.
Essa é a diferença entre um sistema que funciona no demo e um sistema que funciona na vida real:
Nunca confie só na camada de aplicação para garantir integridade de dados. O banco precisa ser a última linha de defesa.
O que o estacionamento me ensinou
Saí daquela noite com uma compreensão diferente do que significa construir software.
Quando você escreve código para um exercício de faculdade, o critério de sucesso é: compila, passa nos testes, entrega. O usuário é um professor que vai rodar casos de teste específicos.
Quando você constrói para pessoas reais, o critério de sucesso é outro. A pergunta não é "o sistema funciona?" — é "o sistema funciona quando Dona Maria de 60 anos, com um celular Android velho e 3G fraco, tenta se cadastrar pela primeira vez às 19h30 com 15 pessoas na fila atrás dela?"
Essa pergunta muda tudo. Muda o que você prioriza. Muda o que você testa. Muda o que você considera "pronto".
Nenhuma aula me ensinou isso. Uma noite num estacionamento ensinou.
O Parusya hoje processa mais de 1.000 check-ins por mês. A equipe usa sem precisar de treinamento. Os dados são confiáveis o suficiente para a coordenação tomar decisões baseada neles.
Mas o que fica de verdade não é o número. É a memória do primeiro bip — e da certeza de que um problema havia deixado de existir.