Uma falha de login causada pela divergência no armazenamento de números de telefone expôs uma vulnerabilidade. A conta de um usuário foi bloqueada porque o banco de dados continha o mesmo número de celular alemão de duas formas — 0171 5550134 em uma linha e +49 171 5550134 em outra — fazendo com que o sistema os tratasse como entradas distintas. O resultado: um OTP nunca chegou, e o botão "enviar código" tornou-se uma loteria.
Por que números de telefone são mais difíceis que datas
Desenvolvedores costumam confiar em uma regex para domar a entrada de números de telefone. Essa confiança se estilhaça no momento em que um número cruza uma fronteira ou um plano nacional muda. Datas seguem um calendário previsível; números de telefone sofrem mutações com operadoras, regulamentações e convenções culturais.
O custo oculto de strings puras
Armazenar um número de telefone como uma string simples parece único — até que duas representações apontem para a mesma linha. Sistemas de OTP que consultam o banco de dados em busca de "o" número acabam enviando um código para um duplicado que nunca chega ao usuário.
O problema piora quando os números estão em campos numéricos. Uma coluna BIGINT remove o + inicial e quaisquer zeros à esquerda, transformando +49 171 5550134 em 491715550134. Sem o sinal de mais, reconstruir o formato original torna-se um palpite.
O contrato E.164
O plano internacional de numeração telefônica, E.164, define uma representação única e portátil:
- Começa com um
+ - Seguido por um código de país de 1 a 3 dígitos
- Depois o número do assinante
- No máximo 15 dígitos no total
- Sem espaços, pontos ou traços
O E.164 não garante que o número esteja ativo; ele apenas garante que a string siga um padrão estrutural válido. Trate-o como um contrato de formato, não como um oráculo de acessibilidade.
Armadilhas comuns que regex não consegue corrigir
- Armazenamento numérico – BIGINT descarta o
+e os zeros à esquerda. Use uma coluna de texto (TEXT ou VARCHAR) em vez disso. - Regex codificadas (hard-coded) – Planos nacionais mudam. O México removeu seu prefixo de tronco em 2019; a Argentina agora exige um
9após o código do país para linhas móveis. Um padrão estático torna-se obsoleto rapidamente. - Remoção cega de zeros – Telefones fixos italianos mantêm um zero à esquerda, os alemães não. Uma regra genérica de "remover zeros à esquerda" corrompe os dados italianos enquanto deixa os números alemães intactos.
- Assumir que formato é igual a entregabilidade – o libphonenumber valida a estrutura, mas não pode dizer se o aparelho está ligado ou se o número foi portado.
Construindo um pipeline confiável
- Peça o país – Adicione um seletor de país nos formulários de cadastro e passe essa região para o parser.
- Mostre a formatação em tempo real – Use a formatação “AsYouType” para que os usuários vejam o padrão correto enquanto digitam.
- Valide no blur – Execute a validação após o usuário sair do campo, em vez de a cada tecla pressionada; isso reduz o atrito.
- Persista apenas a string E.164 – Armazene o número normalizado com o prefixo
+no banco de dados. - Formate na interface – Converta de volta para um layout amigável ao ser humano apenas na UI ou em modelos de e-mail.
Ao migrar dados legados, mantenha as linhas que falharem na validação. Analise cada entrada existente em uma nova coluna, sinalize as falhas e gere um relatório. Exclusões silenciosas criam tickets de suporte que, mais tarde, se transformam em correções dispendiosas.
Um checklist rápido para desenvolvedores
- Armazene números como TEXT/VARCHAR no formato E.164.
- Use a biblioteca libphonenumber do Google; ela lida com a realidade complexa dos planos globais.
- Forneça uma região padrão para usuários que omitirem o código do país.
- Chame
is_valid_numberpara cadastros em tempo real; isso verifica se o número se ajusta às regras regionais. - Use
is_possible_numberao limpar dados em massa; isso captura entradas obviamente malformadas sem rejeitar casos limítrofes.
O tratamento adequado de números de telefone não é um "luxo"; é um pré-requisito para qualquer sistema que dependa de um contato confiável com o usuário. Ao normalizar para E.164 e delegar o parsing para uma biblioteca testada em batalha, os desenvolvedores eliminam uma classe de bugs que corroem silenciosamente a confiança e a receita. Trate números de telefone como dados estruturados, não como texto livre, e deixe que os padrões façam o trabalho pesado.
