Fundador da Revin. Engenheiro de formação, especialista em desenvolvimento de software e produtos digitais.

O código antigo costuma ser a única documentação viva das regras que sustentam o faturamento
Dá para subir um sistema PHP 5.6 para o PHP 8, e na maior parte dos casos que chegam aqui isso sai bem mais barato que reescrever do zero. A parte que muda a conta é a outra metade da pergunta. Subir a versão do PHP e colocar Laravel no lugar do que já existe são duas tarefas diferentes, com riscos diferentes, e é a segunda que costuma engolir um trimestre inteiro.
Antes de escolher, meça três coisas, nesta ordem: quantos arquivos o PHP 8 realmente quebra, quantas das suas dependências não têm mais ninguém mantendo, e quanto do seu faturamento passa por um trecho de código que ninguém consegue rodar sem cliente do outro lado. Dá perto de dois dias de trabalho de um dev que já conhece o projeto. Barato perto do preço de errar essa escolha.

Meia hora de PHPCompatibility troca o "acho que é muita coisa" por uma contagem por arquivo
Teve um cliente que me perguntou se subir o MySQL 5 para o 7 era a mesma coisa que trocar para o Postgres. Respondi pela fruteira, porque ele não era técnico e fruteira todo mundo entende. MySQL 5 é banana prata, MySQL 7 é banana nanica, Postgres é maçã. As três são fruta, os três são banco SQL, e mesmo assim ninguém troca banana por maçã no meio da receita achando que vai dar na mesma.
Sair do PHP 5.6 para o 8 é trocar de banana. A linguagem é a mesma, quase toda a sintaxe que seu time escreveu continua válida, e o trabalho mora em incompatibilidades pontuais e em comportamento que mudou de resultado. Colocar Laravel dentro de um sistema que nasceu procedural é a maçã. O framework decide por onde a requisição entra, como a rota resolve, como o banco é acessado e onde a regra de negócio vive. Isso não é atualizar, é trocar a fundação com o prédio ocupado.
As duas coisas são possíveis. Só que quem promete as duas no mesmo cronograma normalmente entrega a primeira e passa o resto do ano na segunda.
Pare de discutir por impressão. Instale o PHPCompatibility no PHP_CodeSniffer, aponte para a versão de destino (8.2 ou 8.3) e rode no projeto inteiro. Em meia hora você sai do "acho que é muita coisa" para uma contagem de ocorrências por arquivo.
O resultado costuma surpreender para o lado bom. Num sistema de porte médio, o relatório aponta algo em torno de duas a quatro centenas de ocorrências, e boa parte concentrada em poucos arquivos. Quase tudo é mecânico: `each()` que sumiu, `create_function` que foi removido, chamada `mysql_` que já tinha morrido no 7, argumento passado por referência onde não pode mais. O Rector resolve um pedaço grande disso sozinho. O que sobra é trabalho chato de uma tarde, não projeto de trimestre.
O perigo mora no que não quebra. A comparação solta entre string e número mudou no PHP 8. No 5.6, `0 == "abc"` devolvia verdadeiro. No 8, devolve falso. Nada explode, nenhum log acende, o `if` apenas escolhe o outro caminho. Se aquele `if` decide desconto, faixa de frete ou permissão de acesso, sua regra de negócio mudou sem ninguém assinar embaixo.
Por isso a ordem importa. Primeiro você escreve teste de caracterização nos fluxos que geram dinheiro, congelando o comportamento atual mesmo quando ele é esquisito. Depois sobe a versão. Quem faz o contrário descobre a mudança pelo suporte, três semanas depois, num valor errado de nota fiscal.

No caminho do meio o roteador entrega uma rota por vez, e o sistema roda partido por um tempo
Segunda medição, meio dia de trabalho: `composer outdated --direct` e `composer audit`. Some a isso as extensões nativas que o servidor carrega, porque a mcrypt saiu do core no 7.2 e ainda aparece código de criptografia caseiro grudado nela.
É aqui que a decisão vira. Se as dependências diretas têm versão compatível com PHP 8, mesmo exigindo ajuste, existe caminho de atualização e ele é curto. Se o sistema está preso num framework que parou de receber correção há anos, alguma coisa vai ser reescrita de qualquer jeito. A diferença é que agora você sabe disso por medição, e pode reescrever a camada de entrada em vez do sistema inteiro. Reescrever o roteamento de um sistema é uma tarefa. Reescrever as regras de negócio de dez anos é outra, e é a que mata cronograma.
Um detalhe que encurta discussão de diretoria: rodar PHP 5.6 hoje significa rodar sem correção de segurança desde o fim de 2018. Isso não escolhe entre atualizar e reescrever. Tira do jogo a terceira opção, que costuma ser a favorita de todo mundo na reunião, a de deixar como está e olhar isso no ano que vem.
A terceira medição não é cobertura de teste. Liste os quatro ou cinco fluxos que geram receita (cadastro do pedido, cálculo de preço, cobrança, emissão fiscal, integração com quem paga você) e pergunte uma coisa só sobre cada um: dá para rodar esse fluxo do começo ao fim, numa máquina limpa, sem cliente do outro lado?
O número que sai daí decide mais que qualquer discussão de stack. Sistema com fluxo de dinheiro reproduzível aguenta migração de versão tranquilo. Sistema em que a única forma de saber se a cobrança funcionou é esperar o boleto do dia seguinte não aguenta nem migração nem reescrita, porque nos dois casos você vai empurrar mudança no escuro.
E cuidado com o relatório bonito. Já abri projeto com cobertura alta em que os testes rodavam o método e não verificavam nada, sem um `expect` no meio. A métrica existia, a garantia não. Vale o mesmo raciocínio que uso quando alguém herda um sistema legado sem documentação: mede primeiro, escreve depois.
Essa frase apareceu em quase todo comentário da thread que originou este texto, no TabNews, que juntou 35 comentários numa semana. Ela é verdadeira e é o pior argumento possível a favor de reescrever.
O código feio é a documentação viva das suas regras. Aquele `if` esquisito no cálculo de comissão é um acordo que alguém fechou com um parceiro em 2019 e nunca escreveu em lugar nenhum. Quando o time reescreve do zero, ele não reescreve o que está no código. Reescreve o que consegue entender do código, que é sempre menos. O resto reaparece como bug em produção, um por semana, durante meses. É o motivo de eu quase nunca aprovar reescrever um sistema do zero quando me perguntam.
Tem também a conta que ninguém coloca no slide. Reescrita congela o produto. Enquanto o time refaz o que já existe, o concorrente lança feature e o seu comercial vende o mesmo sistema de sempre. Com o custo de um squad em 2026, um trimestre inteiro sem entrega nova para o cliente é dinheiro que você paga duas vezes, na folha e no roadmap parado.
O que dá certo na maioria dos casos é fatiar em três movimentos, nesta ordem:
No terceiro movimento o sistema roda partido por um tempo, e isso incomoda todo desenvolvedor que gosta de coisa limpa. Aguente o incômodo. Software não fica pronto, e o estado partido é reversível a qualquer momento, coisa que uma reescrita de nove meses não é.
Agora o limite, porque conselho que só afirma não vale nada. Esse caminho não serve em três situações. Quando a dependência central do sistema não tem nenhuma versão que rode em PHP 8 e o fornecedor sumiu, você já está reescrevendo, só falta admitir. Quando o negócio mudou tanto que o sistema modela uma empresa que não existe mais, migrar versão só preserva um modelo errado com sintaxe nova. E quando a operação inteira cabe em umas oito telas, o custo de reescrever cai para semanas e a matemática vira, ainda que eu recomende medir antes de acreditar nessa contagem de telas, que sempre aumenta quando alguém abre o código.
Se você vai levar essa decisão para uma reunião esta semana, peça dois números antes de qualquer proposta: quantas ocorrências o PHPCompatibility aponta e quantas dependências diretas não têm versão para PHP 8. Se ninguém do seu time conseguir responder isso em dois dias, o problema não está na escolha entre migrar e reescrever. Está no fato de que ninguém ainda abriu o sistema para olhar.
8 min de leitura
Conteúdos do Artigo: