Alterei uma assinatura de método e agora tenho mais de 25.000 erros. E agora?

166

Recentemente, iniciei um novo trabalho em que estou trabalhando em um aplicativo muito grande (15M loc). No meu trabalho anterior, tínhamos um aplicativo igualmente grande, mas (para melhor ou para pior), usamos o OSGi, o que significava que o aplicativo era dividido em vários microsserviços que podiam ser alterados, compilados e implantados independentemente. O novo aplicativo é apenas uma grande base de código, com talvez duas DLLs.

Então, preciso mudar a interface dessa classe, porque é isso que meu chefe me pediu para fazer. Eles escreveram inicialmente com algumas suposições que não generalizaram muito bem e, por um tempo, evitam o problema da refatoração, porque está muito acoplado. Mudei a interface e agora existem mais de 25000 erros. Alguns dos erros estão em classes com nomes importantes como "XYZPriceCalculator", que realmentenão deve quebrar. Mas não consigo iniciar o aplicativo para verificar se ele está funcionando até que todos os erros sejam resolvidos. E muitos dos testes de unidade referenciam diretamente essa interface ou são acoplados a classes base que fazem referência a essa interface; portanto, apenas corrigi-las é uma tarefa bastante grande por si só. Além disso, eu realmente não sei como todas essas peças se encaixam, então, mesmo que eu pudesse começar, eu realmente não sei como seria se as coisas estivessem quebradas.

Eu nunca realmente enfrentei um problema como esse no meu último emprego. O que eu faço?

user788497
fonte
7
Você precisará nos fornecer mais informações sobre os tipos de erros e o que "é essa classe", não somos leitores de mentes.
Whatsisname
137
"mais de 25000 erros", esses números mostrados no VS geralmente estão errados. Existem alguns erros, mas com a compilação de uma dll frequentemente usada quebrada, outras compilações também falham, aumentando os números de erros astronômicos. Eu sempre começo a corrigir com a primeira mensagem de erro encontrada na saída da compilação, não na lista de erros.
Bernhard Hiller
13
Gostaria de ver as assinaturas antes e depois do método que você alterou. Erros BTW - 25k realmente não parecem muitos para lidar. Tedioso sim, assustador, sim, incontrolável, não.
jmoreno
46
Uma interface pública é um contrato. Não quebre esses contratos - crie novos.
Matthew Leia
6
25000 erros !? Houston, temos um problema. Definitivamente reverta a mudança e converse com seu gerente, explique a ele que você pode ter que criar uma nova interface completamente.
Code Whisperer

Respostas:

349

25000 erros basicamente significa "não toque nisso". Mude de volta. Crie uma nova classe que tenha a interface desejada e mova lentamente os consumidores da classe para a nova. Dependendo do idioma, você pode marcar a classe antiga como obsoleta, o que pode causar todos os tipos de avisos do compilador, mas na verdade não interromperá a compilação.

Infelizmente, essas coisas acontecem em bases de código mais antigas. Não há muito que você possa fazer a respeito, exceto que lentamente melhore as coisas. Ao criar as novas classes, teste-as adequadamente e crie-as usando os princípios do SOLID, para que sejam mais fáceis de mudar no futuro.

Stephen
fonte
79
Não precisa necessariamente ser uma nova classe . Dependendo linguagem e circunstâncias, pode ser uma função, interface, traço, etc.
Jan Hudec
33
Também ajuda se a interface antiga puder ser substituída pelo wrapper de compatibilidade em torno da nova.
Jan Hudec
79
Às vezes, 25000 erros na verdade significam : Nunca ousamos tocar nisso, mas agora que um recém-chegado chegou, vamos dar a ele essa tarefa de limpar estábulos de Augean.
Mouviciel 4/11
13
@theDmi: Eu concordo que 1 alteração e erros de 25k provavelmente significam 2,5k no máximo, e não ficaria surpreso ao descobrir que eram cerca de 250. Muito trabalho de qualquer maneira, mas factível.
jmoreno
33
Esses erros 25000 podem ser corrigidos por uma única alteração. Se eu quebrar a classe base de uma enorme hierarquia, cada uma das classes derivadas emitirá erros sobre a base inválida, todo uso dessas classes emitirá erros sobre a classe que não existe, etc. Imagine que era "getName" e eu adicionei um argumento. A substituição na classe de implementação "HasAName" agora não funciona (erro), e tudo o que é herdado agora gera erros (todas as 1000 classes), bem como toda vez que eu crio uma instância de qualquer uma delas (24x por classe em média). A correção é ... uma linha.
Yakk
80

Divida e conquiste com refatorações

Freqüentemente, dividir a alteração que você precisa fazer em etapas menores pode ajudar, pois você pode executar a maioria das etapas menores de uma maneira que não interrompa o software. As ferramentas de refatoração ajudam muito nessas tarefas.

Dividir

Primeiro, identifique as menores mudanças possíveis (em termos de alterações lógicas, não em termos de LoC alterado) que se resumem à alteração que você deseja obter. Tente isolar etapas que são refatorações puras e que podem ser executadas por ferramentas.

Conquistar

Em casos complicados como o seu, pode fazer sentido realizar uma pequena refatoração de cada vez e deixar o problema descansar, para que todos os sistemas de integração contínua possam verificar a alteração, e talvez a equipe de teste também tenha uma olhada. Isso valida as etapas que você faz.

Para executar uma refatoração específica, você precisa absolutamente de suporte de ferramentas para um caso em que você tenha 25.000 sites de chamada do método que deseja alterar. Talvez pesquisar e substituir também funcione, mas para um caso tão crítico, eu ficaria assustado com isso.

Exemplo

Em C #, por exemplo, é possível usar o Resharper para alterar a assinatura de um método. Se a alteração for suficientemente simples, por exemplo, adicionando um novo parâmetro, você poderá especificar qual valor deve ser usado nos sites de chamada que, de outra forma, apresentariam um erro de compilação.

Você está imediatamente em uma base de código livre de erros e pode executar todos os testes de unidade, que serão aprovados, porque era apenas uma refatoração.

Quando a assinatura do método parecer boa, você poderá substituir os valores que o Resharper adicionou como argumentos ao parâmetro recém-introduzido. Isso não é mais uma refatoração , mas você tem uma base de código livre de erros e pode executar os testes após cada linha alterada.

Às vezes isso não funciona

Essa é uma abordagem muito útil para casos como o seu, nos quais você tem muitos sites de chamadas. E você possui um software em funcionamento agora, portanto, pode ser possível executar pequenas etapas de refatoração para alterar um pouco a assinatura e executar outra.

Infelizmente, não funcionará se a alteração da assinatura for muito complexa e não puder ser dividida em alterações menores. Mas isso é raro; dividir o problema em problemas menores geralmente mostra que é possível.

theDmi
fonte
12
Este. Refatorar se puder (com segurança), caso contrário, você precisará de uma migração adequada.
Sleske #
3
E pelo amor de qualquer pessoa, use as ferramentas de refatoração integradas ao IDE, em vez de apenas alterar a (por exemplo) assinatura do método localmente e depois corrigir os erros resultantes.
precisa saber é o seguinte
36

Esclareça sua tarefa com seu chefe para ajudá-lo a entender o problema e suas necessidades como desenvolvedor de software profissional.

Se você faz parte de uma equipe, procure o desenvolvedor líder e peça conselhos a ele.

Boa sorte.

mmehl
fonte
15
Este é o primeiro passo que eu daria - "Sou júnior e meu chefe me disse para fazer uma coisa e agora estou recebendo uma mensagem de erro muito grande, mas meu chefe não me falou sobre isso". Etapa 1: "Ei, chefe, eu fiz a coisa
certa
28

Não toque. Não cometa nada.

Em vez disso, sente-se em sua cadeira e grite "Heeeeelp !!!!!" o mais alto que puder.

Bem, não exatamente assim, mas peça conselhos a qualquer um de seus colegas seniores. Se você tiver 25.000 erros, não conserta os erros, conserta o que causou os erros. E um colega sênior deve ser capaz de aconselhá-lo sobre como fazer a alteração que seu chefe deseja sem os 25.000 erros envolvidos. Existem várias maneiras de fazer isso, mas o que é bom depende da sua situação específica.

E pode ser que o chefe tenha dito aos colegas seniores que fizessem a mesma alteração e eles disseram "não". Porque eles sabiam o que aconteceria. É por isso que você recebeu o emprego.

gnasher729
fonte
2
Definitivamente, é uma coisa importante a ter em mente. Se um novo funcionário é realmente ambicioso em grande parte, ele pode ser diligente (ingênuo) o suficiente para realizar a tarefa realmente grande que acaba obtendo uma vantagem de memória de 5 bytes ou ser um pouco mais lógico para recodificar e manter posteriormente.
The Great Duck
@TheGreatDuck: Diga-me que você nunca viu uma interface usada em todos os lugares e errada em todos os lugares. Eu tenho certeza.
Joshua
@ Josué, isso nem é relevante para o que acabei de dizer. Há momentos em que corrigir um bug menor nem sempre é a idéia mais inteligente, significa reescrever mais de 10.000 linhas de código. Infelizmente, um novo funcionário pode ser ingênuo o suficiente para tirar 10 noites inteiras do trabalho reescrevendo esse código para impressionar seu chefe e fazê-lo. Claro, é uma ótima coisa a se fazer, mas às vezes basta. Você apenas tem que aceitar a existência do bug.
The Great Duck
1
@ Joshua btw, eu entrei nesta comunidade apenas porque essa pergunta apareceu no meu feed de perguntas populares. Não tenho experiência com design em larga escala como esse. Eu apenas concordei com a visão dessa pessoa.
The Great Duck
22

APIs entrincheiradas não podem ser simplesmente alteradas. Se você realmente precisar alterá-los, anote e / ou documente-os como obsoletos (por qualquer meio que o idioma permita) e documente qual API deve ser usada. A API antiga pode ser desativada lentamente ... talvez muito lentamente, dependendo do seu orçamento de tempo para refatoração.

Kevin Krumwiede
fonte
10

Avalie

Avalie se essa alteração é necessária ou se você pode adicionar um novo método e reprovar o outro.

frente

Se for necessário mudar; então é necessário um plano de migração.

O primeiro passo é introduzir o novo método e fazer com que o método antigo massageie seus argumentos para que ele possa chamar o novo. Isso pode exigir a codificação embutida de algumas coisas; isso é bom.

Este é um ponto de confirmação: verifique se todos os testes são aprovados, confirmados, enviados por push.

Migrar

O trabalho ocupado está migrando todos os chamadores do método antigo para o novo. Felizmente, isso pode ser feito gradualmente, graças ao remetente.

Então vá em frente; não hesite em usar ferramentas para ajudar ( sedsendo as mais básicas, existem outras).

Marque o método antigo como obsoleto (com uma dica de alternar para o novo método); isso ajudará você a descobrir se você esqueceu alguma coisa e ajudará se um colega de trabalho apresentar uma chamada ao método antigo enquanto você estiver trabalhando nisso.

Este é um ponto de confirmação (ou talvez vários pontos de confirmação): verifique se todos os testes são aprovados, confirmados, enviados por push.

Retirar

Depois de algum tempo (talvez apenas um dia), basta remover o método antigo.

Matthieu M.
fonte
4
sedprovavelmente é uma má idéia ... Algo que realmente "entende" o idioma e não faz mudanças drásticas indesejadas é melhor.
Wizzwizz4
1
@ wizzwizz4: Infelizmente, encontrei muito poucas ferramentas que realmente entendem a linguagem o suficiente para C ++; a maioria das ferramentas parece servir para renomear e é isso. É verdade que renomear apenas as chamadas de método exatas (e não nenhuma sobrecarga ou chamada de método sem relação, mas com nomes semelhantes) já é impressionante, mas é insuficiente para algo mais complicado. No mínimo, você precisaria das habilidades para (1) embaralhar argumentos, (2) aplicar uma transformação a um determinado argumento (chamar .c_str()por exemplo) e introduzir novos argumentos. sedmeio que funciona, o compilador pega seus problemas posteriormente.
Matthieu M.
1
sed(ou ed) pode ser adequado para esse tipo de coisa - desde que você revise corretamente o diff antes de se comprometer.
perfil completo de Toby Speight
Este é o TCRR do html, sim? :)
Daniel Springer
8

Se sua alteração na assinatura do método for apenas uma alteração de nome, a solução simples é usar ferramentas para automatizar a alteração nas 25.000 classes que referenciam o método em questão.

Presumo que você tenha simplesmente editado o código manualmente, o que deu origem a todos os erros. Eu também presumo que você esteja familiarizado com Java (consulte sua referência ao OSGi). Por exemplo, no Eclipse (não sei qual ambiente de programação você usa, mas outros ambientes possuem ferramentas de refatoração semelhantes), você pode usar "Refatoração -> Renomear" para atualizar todas as referências ao método, o que deve deixar você sem erros.

Caso esteja fazendo outras alterações na assinatura do método que não sejam simplesmente renomeadas (alterando o número ou tipos de parâmetros), é possível usar "Refatoração -> Alterar assinatura do método". No entanto, é provável que você tenha que ter mais cuidado, como sugerem as outras respostas. Além disso, independentemente do tipo de alteração, ainda pode ser uma tarefa bastante comprometer todas essas alterações em uma base de código ocupada.

MikkelRJ
fonte
2
A OP disse especificamente que o OSGi estava em seu trabalho anterior, portanto não é realmente relevante aqui.
um CVn
3
@ MichaelKjörling Se o OP era um programador Java em seu trabalho anterior, há uma chance melhor do que igual de ser um programador Java neste trabalho também.
Rich
@ MichaelKjörling Eu queria dar uma recomendação concreta, usando o Eclipse para refatoração, porque o OP está familiarizado com Java. Se o OP está de fato usando Java para o projeto atual, eu acho, é menos importante, mas devo esclarecer minha resposta. Obrigado.
precisa saber é o seguinte
6

Aqui está minha contribuição.

Recentemente, iniciei um novo trabalho em que estou trabalhando em um aplicativo muito grande (15 milhões de linhas de código).

Você provavelmente não está familiarizado com o projeto e seus "recursos". Antes de digitar uma única linha de código, é importante estar familiarizado com o projeto. Faça o rollback de suas alterações e comece analisando o código . (Pelo menos o afetado)

Compreender a solução existente oferece uma melhor perspectiva de onde você está entrando. Contextualize a solução e sua importância.

Como o @Greg apontou, você deve poder testar o código existente para ter uma referência válida para comparar (testes de regressão). Sua solução deve ser capaz de gerar os mesmos resultados que os existentes. Nesta fase, você não se importa se os resultados estão corretos ou não . O primeiro objetivo é refatorar, não corrigir bugs. Se a solução existente indicar "2 + 2 = 42", sua solução deverá também. Se isso não gerar exceções, o seu também não deve. Se retornar nulos, o seu também deve retornar nulos. E assim por diante. Caso contrário, você estará comprometendo 25 mil linhas de código.

Isto é por uma questão de retro-compatibilidade.

Por quê? Porque agora, é a sua garantia exclusiva de um refatorador de sucesso.

E muitos dos testes de unidade referenciam diretamente essa interface ou são acoplados a classes base que fazem referência a essa interface.

Uma maneira de garantir a retrocompatibilidade é urgentemente necessária para você. Então, aqui é seu primeiro desafio. Isole o componente para teste de unidade.

Lembre-se de que essas 25k linhas de código foram criadas assumindo os possíveis resultados do código existente. Se você não quebrar essa parte do contrato, estará na metade do caminho para a solução final. Se você faz, bem: que a força esteja com você

Depois de projetar e implementar o novo "contrato", substitua o antigo. Preteri-lo ou retirá-lo.

Sugeri que os erros fossem deixados em paz, pois refatorar e corrigir erros são tarefas diferentes. Se você tentar avançar juntos, poderá falhar nos dois. Você pode achar que encontrou bugs, no entanto, eles podem ser "recursos". Então deixe-os em paz (por um minuto).

25k linhas de código parecem-me problemas suficientes para me concentrar em apenas uma tarefa.

Assim que sua primeira tarefa estiver concluída. Exponha esses bugs / recursos ao seu chefe.

Finalmente, como o @Stephen disse:

Não há muito o que você possa fazer a respeito, exceto que lentamente melhore as coisas. Ao criar as novas classes, teste-as adequadamente e crie-as usando os princípios do SOLID, para que sejam mais fáceis de mudar no futuro

Laiv
fonte
5

Teste-o.

Todo mundo está recomendando como refatorar para que haja pouco impacto. Mas com tantos erros, mesmo que você consiga refatorar com menos de 10 linhas de código (você provavelmente pode), você afetou 25.000 fluxos de código , mesmo que não precise reescrevê-los.

Portanto, a próxima coisa a fazer é garantir que seu conjunto de testes de regressão passe com cores vivas. E se você não tiver um, faça um que o faça. Adicionar um conjunto abrangente de testes de regressão ao seu projeto monolítico parece chato, mas é uma boa maneira de aumentar a confiança nos candidatos a liberação e liberá-los mais rapidamente se o conjunto for bem automatizado.

Greg
fonte