Quando um BIG Reescrever a resposta?

278

Basta ler a pergunta sobre o Big Rewrites e lembrei-me de uma pergunta que eu queria responder.

Eu tenho um projeto horrível passado para mim, escrito em Java antigo, usando o Struts 1.0, tabelas com relacionamentos inconsistentes ou nenhum relacionamento e até mesmo tabelas sem chaves ou campos primários que deveriam ser chaves primárias, mas não são únicas. De alguma forma, a maior parte do aplicativo "simplesmente funciona". A maioria das páginas é reutilizada (código colado e copiado) e codificada. Todo mundo que já trabalhou no projeto o amaldiçoou de uma forma ou de outra.

Agora, há muito que eu pensava em propor à gerência superior uma reescrita total desse aplicativo horrendo. Estou lentamente tentando me divertir, mas realmente sinto que isso merece alguns recursos dedicados para que isso aconteça. Depois de ler os artigos sobre grandes reescritas, estou pensando melhor. E isso não é bom quando quero convencer meus superiores a apoiar minha reescrita. (Eu trabalho em uma empresa bastante pequena para que a proposta tenha a possibilidade de ser aprovada)

TL; DR Quando uma grande reescrita é a resposta e quais argumentos você pode usar para apoiá-la?

Jonn
fonte
1
Além disso, relacionados: programmers.stackexchange.com/questions/20246/…
IAbstract
6
É velho, mas é um clássico - Joel "coisas que você nunca deve fazer, Parte I" joelonsoftware.com/articles/fog0000000069.html
MAWG
Esta é uma grande pergunta. Muito relevante, essa situação ocorre com freqüência na engenharia de software.
Brad Thomas

Respostas:

325

Desculpe, isso vai demorar, mas é baseado na experiência pessoal como arquiteto e desenvolvedor em vários projetos de reescrita.

As seguintes condições devem levar em consideração algum tipo de reescrita. Vou falar sobre como decidir qual deles será feito depois disso.

  • O tempo de aceleração do desenvolvedor é muito alto. Se demorar mais do que abaixo (por nível de experiência) para acelerar um novo desenvolvedor, o sistema precisará ser reprojetado. Por tempo de aceleração, quero dizer a quantidade de tempo antes que o novo desenvolvedor esteja pronto para fazer seu primeiro commit (em um pequeno recurso)
    • Recém saído da faculdade - 1,5 meses
    • Ainda verde, mas já trabalhou em outros projetos antes - 1 mês
    • Nível médio - 2 semanas
    • Experiente - 1 semana
    • Nível Sênior - 1 dia
  • A implantação não pode ser automatizada, devido à complexidade da arquitetura existente
  • Até correções simples de erros demoram muito devido à complexidade do código existente
  • Novos recursos demoram muito e custam muito devido à interdependência da base de código (novos recursos não podem ser isolados e, portanto, afetam os recursos existentes)
  • O ciclo de teste formal leva muito tempo devido à interdependência da base de código existente.
  • Muitos casos de uso são executados em poucas telas. Isso causa problemas de treinamento para usuários e desenvolvedores.
  • A tecnologia em que o sistema atual está exigindo
    • É difícil encontrar desenvolvedores de qualidade com experiência na tecnologia
    • Foi descontinuado (não pode ser atualizado para suportar plataformas / recursos mais recentes)
    • Existe simplesmente uma tecnologia de nível superior muito mais expressiva disponível
    • O custo de manutenção da infraestrutura da tecnologia antiga é muito alto

Essas coisas são bastante evidentes. Quando decidir sobre uma reescrita completa versus uma reconstrução incremental é mais subjetivo e, portanto, mais cobrado politicamente. O que posso dizer com convicção é que afirmar categoricamente que nunca é uma boa ideia está errado.

Se um sistema puder ser reprojetado de forma incremental e você tiver o apoio total do patrocínio do projeto para isso, deverá fazê-lo. Aqui está o problema, no entanto. Muitos sistemas não podem ser redesenhados de forma incremental. Aqui estão algumas das razões que encontrei que impedem isso (tanto técnica quanto política).

  • Técnico
    • O acoplamento dos componentes é tão alto que as alterações em um único componente não podem ser isoladas de outros componentes. A reformulação de um único componente resulta em uma cascata de alterações não apenas nos componentes adjacentes, mas indiretamente em todos os componentes.
    • A pilha de tecnologia é tão complicada que o design do estado futuro requer várias alterações na infraestrutura. Isso também seria necessário em uma reescrita completa, mas se for necessário em um novo design incremental, você perderá essa vantagem.
    • Redesenhar um componente resulta em uma reescrita completa desse componente, porque o design existente é tão fubar que não vale a pena salvar. Novamente, você perde a vantagem se for esse o caso.
  • Político
    • Os patrocinadores não podem entender que um novo design incremental requer um compromisso de longo prazo com o projeto. Inevitavelmente, a maioria das organizações perde o apetite pelo dreno contínuo do orçamento criado por um novo design incremental. Essa perda de apetite também é inevitável para uma reescrita, mas os patrocinadores estarão mais inclinados a continuar, porque não querem dividir-se entre um novo sistema parcialmente completo e um sistema antigo parcialmente obsoleto.
    • Os usuários do sistema estão muito apegados às suas "telas atuais". Se for esse o caso, você não terá a licença para melhorar uma parte vital do sistema (o front-end). Um novo design permite contornar esse problema, pois eles estão começando com algo novo. Eles ainda vão insistir em obter "as mesmas telas", mas você tem um pouco mais de munição para recuar.

Lembre-se de que o custo total da reformulação incremental é sempre maior do que uma reescrita completa, mas o impacto na organização geralmente é menor. Na minha opinião, se você pode justificar uma reescrita e tiver desenvolvedores de superstar, faça-o.

Só faça isso se tiver certeza de que existe vontade política de fazê-lo até o fim. Isso significa a adesão de executivos e usuários finais. Sem ele, você irá falhar. Estou assumindo que é por isso que Joel diz que é uma má ideia. A entrada de executivos e usuários finais parece um unicórnio de duas cabeças para muitos arquitetos. Você precisa vendê-lo de forma agressiva e fazer campanha por sua continuação continuamente até que esteja completo. Isso é difícil, e você está falando sobre apostar em sua reputação em algo que alguns não desejam que seja bem-sucedido.

Algumas estratégias para o sucesso:

  • Se você fizer isso, no entanto, não tente converter o código existente. Projete o sistema a partir do zero. Caso contrário, você está perdendo seu tempo. Eu nunca vi ou ouvi falar de um projeto de "conversão" que não acabou miseravelmente.
  • Migre os usuários para o novo sistema, uma equipe por vez. Identifique as equipes que sofrem mais com o sistema existente e migre-as primeiro. Que eles espalhem as boas novas de boca em boca. Dessa forma, seu novo sistema será vendido a partir de dentro.
  • Projete sua estrutura conforme necessário. Não comece com uma estrutura que eu passei 6 meses construindo - que nunca viu código real.
  • Mantenha sua pilha de tecnologia o menor possível. Não projete demais. Você pode adicionar tecnologias conforme necessário, mas removê-las é difícil. Além disso, quanto mais camadas você tiver, mais trabalho será feito pelos desenvolvedores. Não torne difícil desde o início.
  • Envolva os usuários diretamente no processo de design, mas não permita que eles determinem como fazê-lo. Ganhe a confiança deles, mostrando a eles que você pode dar o que eles querem melhor se seguir os bons princípios de design.
Michael Meadows
fonte
25
Acho que o argumento de Joel é que, ao reescrever, você perde o conhecimento acumulado no código antigo.
quant_dev
14
@quant_dev parcialmente sim, mas quando você reescreve algumas vezes, percebe que muitos dos erros no sistema antigo eram devidos à maneira como o sistema antigo funcionava, não estritamente relacionado à lógica de como o sistema ideal deveria funcionar.
Tjaart
13
Joel diz que falhará porque, durante a reescrita, você não está adicionando nenhuma nova funcionalidade ao seu aplicativo. O único objetivo de uma reescrita é eliminar parte ou toda a dívida técnica. Concedido, isso não é algo pequeno, mas é arriscado se seus concorrentes estiverem adicionando novos recursos e funcionalidades ao software deles enquanto você está apenas se livrando da dívida técnica na sua.
Robert Harvey
25
Eu trabalhei com uma base de código que se encaixava em todos esses critérios + e ainda assim o Big Rewrite ainda falhou e, de certa forma, piorou as coisas ao fornecer código de "novo acordo" semi-implementado para lidar com isso, que era muito diferente do antigo caos, mas não muito mais gerenciável. O IMO, apenas se tornar completamente impossível refatorar, você deve remover todo o foco da adição de recursos e correção de bugs que estão custando a seus clientes hoje. A menos que você esteja falando de um código verdadeiramente indecifrável, uma equipe que não pode extrair bits modulares para uma reutilização mais fácil à medida que avança, provavelmente não poderá re-arquitetar o suficiente para o grande rw.
precisa
11
Essa é uma resposta útil, mas longe de ser fantástica, porque causa uma impressão errada em algumas coisas. Por exemplo, "Lembre-se de que o custo total da reformulação incremental é sempre mais alto do que uma reescrita completa" - a palavra "sempre" nessa frase está incorreta, porque "a reformulação incremental" oferece a oportunidade de reutilizar pelo menos algumas partes do design existente, enquanto uma "reescrita completa" não oferece isso. Eu sei que com certeza, porque eu estava nessa situação em que reescrevemos um aplicativo> 100K LOC parcialmente na íntegra, parcialmente não.
Doc Brown
109

Eu participei de duas grandes reescritas. Primeiro foi um pequeno projeto. O segundo foi o principal produto de uma empresa de software.

Existem várias armadilhas:

  • reescrições sempre levam mais tempo que o esperado.
  • reescritas não têm efeitos / benefícios diretos para o cliente.
  • a capacidade dedicada à reescrita não é usada para dar suporte ao cliente.
  • você perderá a funcionalidade com uma reescrita, a menos que tenha 100% de documentação.

Reescritas raramente são a resposta real. Você pode refatorar grande parte do código sem perder nada e sem muito risco.

Reescrever pode ser a resposta se:

  • você está mudando para outro idioma ou plataforma.
  • você está alternando estruturas / componentes externos.
  • a base de código existente não é mais sustentável.

Mas eu recomendo fortemente a abordagem lenta usando refatoração. É menos arriscado e você mantém seus clientes felizes.

Toon Krijthe
fonte
11
+1 para abordagem de refatoração. Muitas vezes, não há tempo ou horas suficientes para reescrever E manter o sistema existente.
Ryan Hayes
Atualmente, ele é usado para um aplicativo de demonstração (nenhum cliente o comprou recentemente e achei que agora é o melhor momento para recriá-lo).
21410 Jonn
4
@ John: Como executivo, eu teria muita dificuldade em reescrever um aplicativo para o qual minha equipe de vendas ainda não conseguiu atrair um cliente. Com toda a honestidade, eu daria um certo tempo e depois decidiria o que fazer. Se não houver interesse, eu jogaria fora a coisa toda e escolheria outra coisa para fazer.
NotMe 22/09
4
Recentemente, reescrevi um aplicativo Visual Basic em Java. Isso permitiu que ele fosse executado como um serviço no Windows (sem Java GUI) - benefício para o cliente.
4
"reescritas não têm efeitos / benefícios diretos para o cliente" - isso geralmente é um mito, pois as estruturas mais recentes fornecem "incorporadas" muitas melhorias de produtividade que são impossíveis ou muito caras de implementar. Um exemplo, a atualização de um aplicativo vb6 para um aplicativo .net permite que os usuários abram arquivos maiores (devido a uma arquitetura de 64 bits), o que significa que os usuários finais não precisam interromper artificialmente seu trabalho.
Stephen
72

É hora de reescrever quando:

O custo de reescrever o aplicativo + manter o aplicativo reescrito é menor que o custo de manutenção do sistema atual ao longo do tempo.

Alguns fatores que tornam a manutenção da atual mais cara:

  • O idioma é tão antigo que você precisa pagar às pessoas que sabem muito dinheiro para programar nele (COBOL).
  • (por experiência, infelizmente) O sistema está em uma arquitetura de hardware tão antiga que eles precisam vasculhar as peças Ebay e COLLECT para adicionar à máquina em que estão sendo executadas porque não são mais fabricadas. Isso é chamado de "suporte à vida útil do hardware" e é caro porque, à medida que as peças se tornam mais escassas, elas (podem) subir de preço ou (absolutamente) acabarão eventualmente.
  • Tornou-se tão complexo que o //Here be dragons.comentário está em todo o seu código.
  • Você não pode escrever outros projetos e agregar novo valor à empresa, porque está sempre remendando esta fera feia.
Ryan Hayes
fonte
Foi exatamente isso que levou à reescrita da qual participei. O código era tão frágil e o custo de adicionar novos recursos tão alto que não reescrever não era mais uma opção.
precisa saber é o seguinte
16
Estou rindo aqui do ponto # 2.
Paul Nathan
4
@Paul: Eu também fiz quando um cliente me disse isso, depois descobri que eles estavam falando sério ... e que eu estaria cumprindo requisitos para a reescrita. Que dia emocionante que foi.
Ryan Hayes
3
O problema é como você mede os custos.
2
Eu gosto dessa resposta, engraçada e verdadeira. Eu gostaria de adicionar "quantificável" logo antes de "menos". Muitas vezes, é fácil fazer a reivindicação, mas é importante poder quantificar o tempo perdido.
Kevin Hsu
17

Se você precisar de uma alteração fundamental na arquitetura do projeto, talvez seja hora de começar de novo.

Mesmo com isso, haverá potencialmente grandes faixas de código que ainda valem a pena ser reutilizadas em seu novo projeto.

Preste atenção ao aviso justo. Um projeto atual terá suas regras de negócios testadas e refinadas com inúmeras horas de uso real, algo que não será verdadeiro para um projeto iniciado do zero.

Duvido que um período de tempo ou sensação de intestino seja uma medida apropriada de uma medida tão drástica. Você deve ter razões claras , defensáveis e bem compreendidas para participar deste curso de ação.

Dan McGrath
fonte
3
Você precisa ter muito cuidado ao "reutilizar grandes faixas de código", pois isso pode levar ao código legado não utilizado e à estrutura de código insuficiente. Refatorar é uma solução melhor na minha opinião.
precisa saber é o seguinte
1
Acordado. Parte dessa declaração deve ser interpretada como 'refatorar' no seu 'novo' projeto. Ou seja, se você realmente já se comprometeu nesse caminho drástico. Como mencionado por outros, essa é uma raridade extrema que quase nunca deve ser feita.
Dan McGrath
1
+1. Além disso, a reescrita levará tempo, durante o qual a manutenção precisará ser feita, mas as mesmas alterações precisarão ser feitas nas duas bases de código.
Larry Coleman
12

Embora eu concorde com a resposta de Kramii e a opinião de Joel, há momentos em que é apropriado reescrever. Em aplicativos de longa duração (estou falando de 10 a 20 anos ou mais), a manutenção se torna cada vez mais cara ao longo do tempo. Isso ocorre porque o código se torna cada vez mais espaguete, pois a arquitetura original é sacrificada por patches de manutenção rápida. Além disso, os desenvolvedores de tecnologias mais antigas se tornam mais raros e mais caros. Finalmente, o hardware começa a envelhecer e fica cada vez mais difícil encontrar novos hardwares, sistemas operacionais, estruturas etc. para executar o aplicativo antigo. Além disso, as empresas evoluem e, muito provavelmente, um sistema mais antigo não atenderá às necessidades de negócios da organização, assim como um sistema totalmente novo.

Portanto, é necessário ponderar todo o custo de manutenção em andamento, bem como os benefícios potenciais de um novo sistema para os negócios, contra o custo de reescrever a coisa do zero. Seja muito pessimista em suas estimativas sobre o custo de uma reescrita. Quase sempre custa mais e leva mais tempo do que você imagina.

RationalGeek
fonte
+1 por mencionar a Lei de Hofstadter .
Baelnorn 14/01
11

Pare! Reescrever quase nunca é a resposta. Mais frequentemente, refatorar é uma aposta melhor.


Claro, existem são momentos em que uma reescrita é justificada:

  • Você está mudando para uma nova plataforma na qual as ferramentas de migração não existem (ou não podem ser gravadas de maneira barata).
  • O aplicativo a ser reescrito é trivial.
  • O código-fonte do aplicativo original é perdido e a recuperação é mais cara que a reescrita.
  • A grande maioria das regras de negócios encapsuladas pelo aplicativo existente não se aplica mais.
  • Existem poucos usuários ativos do código existente.
  • Você tem o recurso (tempo, talento e ferramentas) para realizar a reescrita.
  • O aplicativo existente não pode ser executado em um ambiente de produção, por razões legais ou práticas.

Para entender por que recomendo refatorar em vez de reescrever, considere o que está envolvido em uma reescrita. Você deve:

  1. Entenda as nuances do que o aplicativo existente faz. Isso geralmente não é trivial quando você leva em consideração todas as regras sutis de negócios que ele encapsula, o ambiente (humano e técnico) em que opera e as vantagens e desvantagens da solução atual. Na maioria das vezes, o único local em que essas informações existem (se houver) é o código-fonte do aplicativo existente. É lamentável que uma das principais razões para a reescrita seja a dificuldade de entender e manter o código existente.

  2. Reproduza essa funcionalidade (ou uma versão atualizada dela) em um novo aplicativo testado e confiável. O código existente pode não ser entendido pelos desenvolvedores, mas geralmente é bem entendido pelos usuários. Pode não atender às necessidades atuais dos negócios, mas eles podem pelo menos informar o que o aplicativo faz em várias circunstâncias.

As grandes vantagens da refatoração são as seguintes:

  • Você pode levar as coisas um pedacinho de cada vez.
  • Quaisquer alterações podem ser testadas no contexto do aplicativo em funcionamento existente.
  • Hipóteses sobre o funcionamento do código existente podem ser testadas fazendo pequenas alterações e observando o que acontece.
  • As alterações geralmente podem ser entregues aos usuários em fases, e não todas de uma vez.
  • Aprender nos estágios iniciais da refatoração pode informar os estágios posteriores da refatoração.
  • Se você abandonar o processo no meio do processo, ainda haverá benefícios em termos de uma base de código mais limpa (em oposição a uma reescrita que deve ser concluída para oferecer benefícios ao usuário ou aos desenvolvedores).

Lembre-se também de que, se você reescrever, é garantido que você introduzirá muitos novos bugs e bagunça na nova base de código.

Kramii
fonte
2
Você poderia expandir essa resposta explicando por que refatorar mais frequentemente do que não é melhor do que reescrever? E quando seria uma reescrita ser a resposta?
gablin
Dado o fato de que uma reescrita na maioria das vezes é apenas a mesma bagunça em uma roupa diferente, você está certo. Se você pode eliminar esse único problema, está errado. Não há absolutos quando as pessoas certas fazem isso.
JensG #
10

Este gráfico pode ajudar, é uma função da qualidade da base de código e do valor comercial do aplicativo:

insira a descrição da imagem aqui

O gráfico pretende ser um guia sobre quando uma reengenharia de software legado é justificada e quando não é. Por exemplo, se o software tiver alto valor comercial e a qualidade do código for ruim, uma reengenharia será justificada.

user61852
fonte
4
Por que você investiria na reescrita de um projeto com baixo valor comercial? Apenas descarte!
Jørgen Fogh
É por isso que ele "substitui ou ...". Você pode substituir por algo que tenha valor. Ou transformá-lo em algo que tem valor. Mas você tem razão. Vou editá-lo para adicionar a opção "sucatear".
Tulains Córdova
8

Acho que estou na única situação na minha carreira em que a grande reescrita é a resposta:

Fusão da empresa, enorme sobreposição na funcionalidade dos sistemas. Muitos, muitos sistemas foram mesclados e aposentados, e outros ainda estão em processo.

Eu herdei um sistema da outra empresa que ainda vive. Ele possui uma enorme base de códigos, que costumava suportar vários departamentos muito semelhantes aos nossos, mas com um design e estruturas completamente diferentes. Resta apenas um setor comercial, que ganha dinheiro suficiente para manter essa coisa viva em um estado de zumbi. Toda a antiga experiência se foi, não há documentação. A carga de suporte é alta, com falhas a cada semana. Ele não foi incorporado aos sistemas de nossas empresas, porque nossa empresa nunca deu suporte a esse setor específico dos negócios, portanto, não temos a funcionalidade ou o conhecimento.

Parece que este é o único caso em que a reescrita é necessária. Parece que vou ter que descobrir esse gigante, extrair os bits de funcionalidade dedicados a esse negócio e reescrever as partes que precisam ser adicionadas aos nossos sistemas existentes. Feito isso, nossos sistemas existentes podem apoiar esse novo setor, e essa fera pode ser eliminada de sua miséria. Caso contrário, vou perder minha sanidade.

Jay
fonte
Parece que seu empregador precisa conversar com os clientes e explicar a situação para descobrir qual a melhor maneira de ajudá-los, mesmo que isso signifique que eles precisam pagar mais inicialmente, porque economizarão a longo prazo.
7

Trabalhei para uma pequena empresa de software que tinha alguns aplicativos DOS atualizados para lidar com o Y2K, reescritos como um aplicativo Windows de 16 bits e totalmente reescritos como um aplicativo de 32 bits com um recurso 'pequeno' adicional (utilizado apenas por um cliente) que impactou toda a estrutura.

Mover o código de 16 bits para 32 poderia ter sido feito em um mês por uma pessoa, mas NOOOOOOOOO, tivemos que reescrevê-lo para tornar o Sooooooooo muito melhor. Essa coisa poderia ser adaptada para outras indústrias, teria especificações completas e código psuedo antes mesmo de começar. As especificações foram criadas, mas demorou tanto tempo que nem houve tempo para escrever o código real. Foi lançado tarde, com mais bugs do que o de 16 bits 'iniciado' (foi na v.3.0 e, finalmente, ao ponto em que quase chegamos a uma semana sem que alguém relatasse um novo bug).

Você pensaria que reescrever o mesmo aplicativo de 3 a 4 vezes traria algumas melhorias, mas não acho que um front-end da GUI possa ser justificado tanto assim.

Este foi meu primeiro trabalho em TI como chefe de suporte técnico. Eu deveria escrever um livro sobre como não desenvolver software para distribuição. Obviamente, cometemos muitos erros, mas o fato de continuarmos reescrevendo aplicativos aumentou a incompetência.

JeffO
fonte
5

Eu tive um caso muito parecido com o seu, mas o código nem estava usando Struts. Em vez disso, o que fiz foi direcionar as áreas que eram particularmente ruins e causar muitos problemas. Essa abordagem direcionada nos permitiu melhorar cada vez mais.

Durante um período de 2 anos, trabalhamos na refatoração de pedaços e peças juntamente com o trabalho de aprimoramento. Sempre trabalhamos uma tarefa de 'proteção' em um plano de projeto. Ao focar nas áreas específicas que não funcionaram bem, obtivemos o máximo de retorno. O material que funcionou, deixamos em paz. Também crítico é que este trabalho foi realizado no curso do desenvolvimento normal e foi lançado. O problema de uma grande reescrita é que você sai por um ano ou mais e, quando volta, tudo muda de qualquer maneira, e alguns dos erros desagradáveis ​​são suavizados e você perde seu ROI.

Nós nunca reescrevemos a coisa toda. No entanto, paramos de usar essa plataforma para novos trabalhos e lançamos uma nova plataforma para um grande projeto novo. Isso foi aprovado e entregamos um ótimo produto em um período de tempo razoável.

Bill Leeper
fonte
5

Então, aqui estou eu sentado na minha mesa, comecei a reescrever o código dessa bagunça absoluta de um grande arquivo aspx, o banco de dados por trás dele, e a substituição da interface do MS Access para o MsSQL.

Este programa asp está cheio de coisas como

  • include(close.aspx) que possui uma linha de código que fecha a última conexão de banco de dados aberta.
  • Código legado comentado aleatoriamente
  • Sem consideração pela segurança
  • Código de espaguete, milhares de linhas. Tudo em um arquivo.
  • Funções e variáveis ​​sem significado claro por trás de seus nomes

Se precisarmos fazer uma pequena alteração, é como jogar cinco jogos simultâneos de kal-toh no modo pesadelo.

Fui contratado para replicar a funcionalidade e criar um produto que pudesse ser personalizável e vendável para outras pessoas do setor. O problema é que essa coisa foi escrita nos últimos 10 anos para preencher todas as necessidades de negócios (bem, eu diria cerca de cinco ou seis sigma delas).

Se não quiséssemos vender o produto, eles provavelmente não precisariam de uma reescrita, pois fazem a maior parte do que eles querem - talvez não com elegância, mas não era prudente gastar o dinheiro na criação de um bom código 'Faça a mesma coisa.'

Incógnito
fonte
4

Joel Spolsky tem um excelente artigo sobre isso: Coisas que você nunca deve fazer, Parte I

Do título que você pode dizer, é meio unilateral (ele fala sobre por que você nunca deve lançar código) IMO, há muita verdade, recentemente vi um vídeo do canal9 no 25º aniversário do Excel, onde alguns desenvolvedores disseram como ainda hoje, se você visse o código-fonte, verificaria a revisão e acabaria voltando ao código usado pelo Excel que foi escrito há 20 anos.

Você não pode ser 100% de certeza (quando até mesmo Netscape comete erros (de Joels artigo)), eu me senti como o artigo do Joel foi Deus enviou, porque eu posso ser pessimista e amor jogando fora pensamento código que eu sempre pode escrevê-lo melhor uma segunda tempo, mas só agora percebi que isso custa muito .

Para dar uma resposta concreta, eu apenas diria que você precisa fazer uma análise completa de custo x valor .

Meu mundo real: um aplicativo silverlight que estou desenvolvendo a v0.6 até agora tem uma confusão de chamadas assíncronas que tornam o código tão complicado. Desde que descobri as Extensões reativas esta semana, quero realmente reescrever a maior parte do código, mas agora o que digo a meu cliente? O programa funciona perfeitamente bem (com alguns vazamentos de memória), mas eles não se importam? Não posso dizer a eles: Oh, estou tomando mais 2-3 semanas porque quero refazer alguma coisa. No entanto, estou indo para ramificar o código e reescrever / jogar com ele no meu tempo livre .

Apenas meus 2 centavos ok !?

gideon
fonte
Sua primeira implementação é frequentemente subótima porque existem coisas que você ainda não sabe ao projetar. Isso geralmente será refatorado em forma, em vez de recomeçar, pois você provavelmente fez algo certo. Caso contrário, é uma prova de conceito, e ELES frequentemente precisam ser descartados.
+1 para ramificação. Muitas pessoas dizem "não reescrevam" porque parece que você está jogando fora o código antigo - mas não está. Você pode ter bases de código paralelas.
precisa saber é o seguinte
Para ser justo, se você é uma pessoa que pede conselhos a Joel Spolsky, não deve reescrever completamente :-) Caso contrário, você deve reescrever se puder convencer todos os interessados ​​por que os argumentos de Joel não contam no seu caso específico.
gnasher729
3

A solução existente não é escalável.

Estou olhando para você, MS Access.

user21007
fonte
Não tenho certeza se você está olhando para o caminho certo. Jet Red não foi feito para aumentar, AFAIK.
JensG
Como isso responde à pergunta?
Gnat #
3

Segundo Joel, grandes reescritas são o pior erro estratégico que uma empresa pode cometer:

Coisas que você nunca deve fazer, parte I

... É importante lembrar que, quando você começa do zero, não há absolutamente nenhuma razão para acreditar que fará um trabalho melhor do que na primeira vez. Primeiro de tudo, você provavelmente nem tem a mesma equipe de programação que trabalhou na versão um, então na verdade você não tem "mais experiência". Você apenas cometerá a maioria dos erros antigos novamente e apresentará alguns novos problemas que não estavam na versão original.

O velho mantra construído para jogar fora é perigoso quando aplicado a aplicações comerciais em larga escala. Se você estiver escrevendo código experimentalmente, convém criar a função que você escreveu na semana passada, quando pensar em um algoritmo melhor. Isso é bom. Você pode refatorar uma classe para facilitar o uso. Tudo bem também. Mas jogar fora todo o programa é uma loucura perigosa, e se a Netscape realmente tivesse alguma supervisão de adultos com experiência na indústria de software, eles talvez não tivessem atingido tanto o pé.

user3287
fonte
5
Sim. Eu estava procurando alguns contra-argumentos para isso. Tem que ser feito algumas vezes.
22410 Jonn
3
Todo argumento de reescrita não é completo sem uma referência ao artigo de Joel. O que eu digo, que o artigo tem alguns pontos positivos, mas caramba, pense por si mesmo. Talvez você deva dizer por que concorda com o artigo. Não concordo com Joel pessoalmente - simplesmente porque às vezes é melhor cavar um novo buraco do que se enfiar ainda mais na cova que acaba com o sofrimento. De qualquer forma, uma reescrita, acho, traria à tona esses processos de negócios latentes para reavaliação.
Ahmad
O artigo de Joels é bom, pois explica cuidadosamente como as coisas podem piorar.
6
Joel usa um exemplo do navegador Netscape Navigator. Esse é um produto de consumo que estava no meio de uma grande curva de crescimento, trabalhando contra a concorrência com um grande orçamento. Foi um erro estratégico para a Netscape. Por outro lado, projetos de software "corporativos" personalizados internos são diferentes. Você não está correndo para ganhar participação de mercado. Não há perigo de todos os usuários deixarem para um produto competitivo. Tudo se resume a uma decisão comercial: o custo de uma reescrita se pagará a longo prazo e / ou é a melhor maneira de atingir as metas de negócios?
Scott Whitlock
Eu pensei que Joel bateu o conceito-chave, de "Você nunca sabe o que as decisões não foram documentados que você vai ter que aprender da maneira mais difícil"
MathAttack
2

Nunca, refatorar sempre - a verdade é que se o código foi escrito por você - você não poderá fazer melhor.

A menos que você queira mudar a tecnologia, o código não possui nenhuma estrutura (eu o vi há muito tempo em algum site PHP, o autor simplesmente copia / cola spahgetti em vez de include / class / function) ou você assumiu algo de outra pessoa que muito mal escrito.

Tudo deve ser projetado como uma caixa preta. API simples e modular, o que há dentro ... isso é menos importante :) Se você tem espaguete, talvez seja possível fechá-lo dentro de uma caixa preta, para que não contamine um bom código.

Slawek
fonte
Impressionante, Slawek! extremeprogramming.org/rules/refactor.html
Lukas Eder
+1 Gosto da sua opinião, gostaria de saber sua opinião sobre como reescrever um novo conjunto de API? (Veja minha resposta abaixo)
Gideon
Sim, esses são bons pontos. A Microsoft reescreveu o código winXP e eles não conseguiram nem excluir / copiar arquivos na primeira versão comercial do Vista. Enquanto quando eles estavam apenas progredindo em sua base de códigos, obtíamos uma qualidade melhor constantemente (W3.11 => W95 => W98 => ME => XP), o Vista no qual eles reescreveram muitas partes essenciais foi um desastre. Para a nova API ... eu separaria o código atual para ter o máximo possível, e usaria a nova API na camada superior. Por exemplo. suas classes principais permanecem como estão, mas a integração é feita usando a nova API. A menos que tudo é tão confuso que nada mais poderia ser feito do que começar do 0.
Slawek
1
"... a verdade é que se o código foi escrito por você - você não poderá fazer melhor." Iria votar este post se eu tivesse representante suficiente. Isso é derrotista, irreal e implica que, de alguma forma, as pessoas não podem progredir ao ponto onde o código que escrevem é uma melhoria no código que escreveram no passado.
JᴀʏMᴇᴇ
1
@ JᴀʏMᴇᴇ: Concordou - se você não aprendeu nada e não adquiriu experiência ao implementá-lo pela primeira vez e / ou se não puder fazer um trabalho melhor agora que possui mais conhecimento / experiência / retrospectiva; então você é uma batata e não um programador.
Brendan
2

Eu acho que o principal motivo que justifica as reescritas são as mudanças na plataforma. Por exemplo, se você tiver um aplicativo GUI da área de trabalho do Windows e o proprietário da empresa decidir que deseja que a próxima versão seja um aplicativo baseado na Web. Embora nem sempre, na minha experiência na maioria das vezes, isso exigirá uma reescrita, a menos que os desenvolvedores originais escrevam código muito modular e reutilizável (dificilmente acontece).

Craig
fonte
2

Existe a piada clássica sobre "se eu estivesse indo para o XI não começaria daqui".

Peguei um emprego uma vez, onde a principal motivação do empregador era trazer talentos a bordo para lançar uma reescrita há muito tempo atrasada de um aplicativo crítico para os negócios. A pergunta que não fiz foi: por que você acabou nessa situação em primeiro lugar? Deveria ter sido uma bandeira vermelha, se a causa foi

  • muita pressão para adicionar funcionalidade para manter e refatorar gradualmente a fera,

  • pouca capacidade no departamento,

  • muito pouco comprometimento dos clientes ou da gerência para trabalho incremental,

ou seja o que for, e isso causa apodrecimento até o problema atingir um nível intolerável.

Então, eu vou concordar com Joel em que uma reescrita maciça é provavelmente uma péssima idéia - a menos que você tenha evidências firmes de que todas as razões subjacentes às quais uma reescrita principal parece tão necessária foram tratadas , você provavelmente vai repetir o problema no devido tempo.

Julia Hayward
fonte
2

É a resposta quando a reescrita permite que a organização siga uma estratégia que ofereça uma vantagem competitiva ou que atenda melhor seus clientes de alguma maneira que a arquitetura atual não possa acomodar.

Em vez disso, as reescritas geralmente acontecem para aliviar as preocupações de gerenciamento:

  • tudo deve estar no .NET,
  • nossos desenvolvedores dizem que nosso código é péssimo,
  • estamos ficando para trás tecnicamente,
  • não conseguiremos encontrar recursos para dar suporte ao nosso sistema ou, muitas vezes,
  • Faz dez anos, então é hora.

A maioria dessas preocupações não se concretiza e, se o fizessem, poderiam ser tratadas. Esse último, no entanto, é o pior. É essencialmente: não temos um motivo.

Sim, como o carro, um sistema começará a mostrar sinais de desgaste. Isso pode ser aliviado pela manutenção de rotina. Você sabe o que eles dizem sobre como um pouco de manutenção preventiva percorre um longo caminho. Como investimento, a manutenção de rotina (ou seja, refatoração e padronização) quase sempre acarreta um custo menor do que uma reescrita.

No entanto, precisamos antecipar que uma reescrita será eventualmente necessária. Defina datas e planeje-o provisoriamente, mas à medida que a data se aproxima mais, é possível realizar outras metas estratégicas até que você tenha um motivo convincente como ...

Nos últimos anos, perdemos a oportunidade de ganhar grandes contas, porque não podíamos acomodar suas necessidades em tempo hábil ou caro. Nosso sistema será reescrito usando uma arquitetura extensível que permita que as personalizações sejam conectadas (e não codificadas como fazemos atualmente). Isso diminuirá drasticamente o tempo e o custo de acomodar clientes com necessidades especiais. Ganharemos mais contas e atenderemos melhor às necessidades de nossos clientes atuais.

Mario T. Lanza
fonte
Parece que você deveria refatorar nessa direção. Ao refatorar, você deve criar a flexibilidade necessária para facilitar a gravação do próximo recurso.
Ian
1

Ao mudar para uma tecnologia completamente nova, é necessário fornecer a funcionalidade desejada.

"Completamente novo": se você planeja reescrever, mas usa a mesma plataforma, a refatoração e a reestruturação criteriosa são quase certamente a melhor solução. "Plataforma", como usada aqui, é um tanto vaga - considere incluir linguagem e talvez o sistema operacional (estendendo-se do Linux ao Windows ou vice-versa), mas provavelmente não a estrutura (por exemplo, substituindo o Struts pelo Spring).

"Necessário para fornecer a funcionalidade desejada": por volta de 2000, iniciei um projeto para reescrever um componente principal do servidor em Java a partir do C ++ para permitir o encadeamento pronto para uso, um cache de objetos e transações controladas pelo cliente. Naquela época, havia várias bibliotecas de encadeamento para C ++ que precisávamos suportar, e a habilitação de encadeamento grande parte do código do banco de dados exigiria uma reescrita quase total. Quanto às transações controladas pelo cliente ... não acontecerão com a arquitetura antiga.

Mesmo assim, não tenho certeza de que teria reescrito, exceto que tinha conhecimento detalhado do comportamento do sistema atual e era um código relativamente limpo que não havia desenvolvido muitas verrugas em seus 11 anos de vida.

Anon
fonte
0

Para decidir o ponto em que você deve recomeçar, você precisa descobrir onde o valor de continuar em seu projeto atual fica aquém do valor em recomeçar.

A razão pela qual isso é tão difícil é que você faça uma estimativa precisa da velocidade de desenvolvimento da equipe no novo projeto até que você realmente o inicie. Dito isto, se, usando uma estimativa MUITO conservadora de sua velocidade de desenvolvimento no novo projeto, estima-se que o projeto ofereça um retorno sobre o investimento valioso, você terá um caso de negócios para começar de novo.

Ninguém
fonte
0

Com minha métrica, você precisa atender a duas condições para justificar uma reescrita:

  1. Após a reescrita, os novos recursos ficarão mais fáceis / rápidos / com menos erros de gravação
  2. A reescrita levará um tempo menor ou igual ao adicionar o novo recurso atual.

Mesmo assim, você deseja limitar o escopo de sua reescrita. Por exemplo, tínhamos algum software legado em uma empresa que foi escrita em C ++ com a mentalidade de um programador de C que não sabia sobre modularidade. O resultado final foi o código speghetti na forma de uma máquina de estados finitos que abrangia várias classes e DLLs. Tínhamos um novo requisito que era muito diferente das suposições embutidas no código e faria parte do valor agregado patenteado da empresa para seus clientes.

Depois de passar um tempo com o código implementando outros recursos, tive uma idéia muito boa de quanto tempo levaria para descobrir qual das várias instruções de chave eu precisaria alterar etc. Minha proposta era reescrever a seção de código que era a enorme máquina de estados finitos - levaria menos tempo do que estender o código existente. Consegui consolidar em uma DLL o que costumava ser três, e fornecer uma estrutura muito mais orientada a objetos que facilitaria muito a execução da nova funcionalidade crítica, além de facilitar a adição de novas funcionalidades.

Havia três outros aplicativos usando as bibliotecas que precisavam ser reescritos, então eu não queria ter que reescrever completamente esses programas. O escopo era limitado à biblioteca e o que era necessário para vincular a biblioteca ao software existente. Minhas estimativas não foram muito longe e o trabalho se pagou. Logo após o redesenho, fui encarregado de adicionar um novo recurso. O que teria me levado uma semana com a estrutura antiga levou apenas um dia com a nova estrutura.

Berin Loritsch
fonte
0

O OP não indica o escopo do projeto, mas a pista principal que tomei foi "escrita em [idioma / dialeto] antigo usando [libs] antigas" que, para mim, são os principais drivers de uma reescrita. De fato, a maioria dos projetos em que tenho alguma longevidade significativa no mercado acaba percebendo que a atualização de compiladores / intérpretes / sistemas operacionais é uma tarefa importante para manter a base de código.

Em resumo, planejaria a reescrita em fases, priorizando primeiro a atualização nas bibliotecas. Pode ser que, ao longo do caminho, você possa se infiltrar em outras otimizações, mas o fato de planejar adiar algumas alterações para uma fase posterior pode ser uma ótima maneira de manter o cronograma e evitar "ficar preso".

MarkHu
fonte
0

Bem, eu posso imaginar cenários hipotéticos em que eu possa simplesmente jogar fora a base de código existente (situações hipotéticas em que as bolas de bucky têm peso e volume zero, onde ninguém se importa se perdermos semanas ou meses de produtividade enquanto lutamos para adicionar recursos e erros negligenciados corrige o sistema, e onde o sistema é tão trivial e odiado por uma organização que posso substituir a coisa toda e o exército de servidores pelo bloco de notas ++ e um netbook em dez minutos e aumentar a produtividade e a moral de todos.)

Para praticamente qualquer cenário do mundo real, eu recomendaria apenas a refatoração. ^ _ ^. Há muitos custos ocultos e imprevistos para reescrever quando os requisitos legais e de negócios não documentados começam a aparecer e o último minuto de hackers juntos no último minuto começa a acontecer.

== Refatoração no local para maior bem e outras coisas ==

Refatorando o código legado com a abordagem incremental antiga regular,

  1. Se você tiver a chance de converter para UML e documentar a pouca arquitetura que existe.
  2. Se você estiver no controle de versão, procure gerar alguns relatórios de rotatividade de código e descobrir quais arquivos e seções de arquivo foram alterados com mais frequência. Tome nota disso, pois provavelmente serão as áreas com as quais você deseja lidar primeiro.
  3. Antes de alterar qualquer código, tente sempre adicionar alguma cobertura de teste (até mesmo os funcionais feios pacotes funcionais). Se você estiver lidando com uma lógica de extração de código processual grande e confusa, pretende mudar para algum método ou função com nome razoável e, se possível, adicione alguns casos de teste que verifiquem seu novo método. faça de qualquer maneira um truque horrível que você precise e, se possível, paramatize a alteração, se for algo geralmente ajustado como largura da margem ou título, para que seja um pouco mais fácil atualizar a próxima vez.
  4. Use o padrão do adaptador e trabalhe para ocultar os bits mais difíceis do código legado sob um tapete de classes e métodos nomeados logicamente, para que, nas tarefas mais comuns que você e os outros desenvolvedores executem, não precise se preocupar com os bits assustadores que estão por trás suas costas por trás dos métodos e classes pequenos e limpos que você escondeu por trás desse código legado - como aquelas famílias legais que mantêm deformados ex-zumbis assassinos em celeiros para que não estraguem o dia-a-dia dos Fazenda . . . normalmente.
  5. Enquanto você continua a limpar e a limpar, as seções do código continuam a aumentar sua cobertura de teste. Agora você pode aprofundar ainda mais e "reescrever" ainda mais o código quando necessário / desejado com uma confiança cada vez maior.
  6. Repita ou aplique abordagens de refatoração adicionais para continuar a melhorar sua base de código.

Ramificação por abstração

  1. Defina o ponto do problema no código que você deseja remover (a camada de persistência, o gerador de pdf, o mecanismo de registro de faturas, o gerador de widget etc.).
  2. execute (escreva se necessário) alguns casos de teste funcionais (automatizados ou manuais, mas você sabe automatizados) na base de código que tem como alvo essa funcionalidade, juntamente com o comportamento geral.
  3. Extraia a lógica relacionada a esse componente da base de origem existente para uma classe com alguma interface razoável.
  4. Agora, verifique se todo o código está usando a nova interface para executar a atividade X, que antes era dispersa aleatoriamente por todo o código (cumpra a base de códigos, adicione um rastreio à nova classe e verifique as páginas que deveriam estar chamando, etc.) e que você pode controlar qual implementação será usada modificando um único arquivo. (registro de objeto, classe de fábrica, qualquer que seja IActivityXClass = Settings.AcitivtyXImplementer ();)
  5. execute novamente os casos de teste funcional que verificam que tudo ainda está funcionando com toda a Atividade X lançada em sua nova classe.
  6. Escreva testes de unidade sempre que possível em torno da nova classe de atividade X wrapper.
  7. implemente uma nova classe com lógica espaguete menos insana do que a implementação herdada que adere à mesma interface que a classe herdada.
  8. verifique se a nova classe passa nos testes de unidade que você escreveu para a classe herdada.
  9. atualize seu código alterando o registro / factorymethod / o que quer que seja para usar a nova classe em vez da antiga.
  10. verifique se seus testes funcionais ainda passam.

Princípio fechado aberto e uma camada comum de lógica / persistência comercial

Até certo ponto, talvez você consiga se livrar da camada de apresentação, negócios e persistência e escrevendo um novo aplicativo que seja totalmente compatível com a solução herdada ou, no mínimo, ainda possa lidar com os dados introduzidos pela solução herdada. Provavelmente, eu não recomendaria essa abordagem, mas às vezes é um compromisso razoável de tempo / cronograma / recursos / nova funcionalidade necessária.

  • Separe, no mínimo, a camada de apresentação das camadas de negócios e persistência.
  • implemente uma nova interface do usuário e uma melhor camada de apresentação que use a mesma camada comum de negócios e persistência.
  • Verifique se os dados criados com a nova interface do usuário não quebram a interface do usuário antiga. (você estará em água quente se os usuários da nova ferramenta interromperem os usuários da ferramenta antiga). Se você deseja obter compatibilidade total com versões anteriores, salve tudo nas mesmas camadas de persistência. Se você deseja apenas compatibilidade direta com a nova ferramenta, use um novo banco de dados e novas tabelas ou tabelas de extensão para rastrear dados que não estão no sistema legado.
  • Para novas funcionalidades e camada de persistência, adicione novas tabelas e métodos, não altere a lógica herdada existente nem adicione colunas, restrições às tabelas existentes. por exemplo, se você precisar começar a rastrear o contato de emergência dos empregadores e alguns outros campos não modificarem a tabela de funcionários existente (não temos idéia de quais suposições os dados herdados fazem sobre essa tabela), adicione uma tabela de extensão employee_ext id, employee_id, emergency_contact_id, etc_id.
  • Migre lentamente os usuários para o novo sistema. se possível, coloque o sistema legado no modo somente leitura ou adicione algum aviso informando aos usuários que ele não estará mais disponível após a data X.
  • implementar qualquer funcionalidade perdida de alta prioridade ou requisitos de negócios na nova interface do usuário
  • rolar sobre a base de usuários.
  • continue limpando a lógica de negócios e os usuários da camada de persistência, outras metodologias de refatoração.
Keith Brings
fonte
0

Eu diria que existe uma terceira categoria no espaço Refactor vs Rewrite ... E isso está atualizando suas versões de idioma, compiladores e bibliotecas ... Às vezes, apenas a adoção de técnicas modernas de codificação traz um grande benefício.

Pegue o C #, por exemplo, o código v7 escreve muito mais limpo, mais seguro e mais conciso que o v2. Coisas como o Elvis e o operador coalescente nulo ajudam muito.

Alternar compiladores também pode dar vida nova a códigos mais antigos. Assim como as novas bibliotecas que podem facilitar as coisas para trabalhar e implementar ... A rede é um ótimo exemplo de um espectro de dificuldades de implementação.

Também - sistemas linux embutidos ... Pense em instalar novas ferramentas - mude para o git a partir do svn. ou adicione o Vi ao seu sistema, etc.

Nem sempre é necessário refatorar ou reescrever. Sua arquitetura provavelmente está precisando de melhorias, mas funciona ... talvez você só precise pensar em como seu código é escrito etc.

visc
fonte
-2

Acho que nunca vi pessoas reescrevendo um aplicativo herdado.

O que acontece é que as pessoas estão escrevendo aplicativos totalmente novos com uma arquitetura, tecnologias, etc. diferentes, que têm alguns recursos em comum com a geração anterior. E é chamado app 2.0, mas na verdade tem muito poucas coisas em comum com o original.

Mas isso não é realmente uma "reescrita" do original, no sentido de que você está tentando alcançar a paridade de recursos.

Xavier T.
fonte