Como manter um produto de software grande e complexo com manutenção ao longo dos anos?


156

Trabalho como desenvolvedor de software há muitos anos. Foi minha experiência que os projetos se tornam mais complexos e insustentáveis ​​à medida que mais desenvolvedores se envolvem no desenvolvimento do produto.

Parece que o software em um certo estágio de desenvolvimento tem a tendência de ficar "mais hackier" e "hackier", especialmente quando nenhum dos membros da equipe que definiu a arquitetura trabalha mais na empresa.

Acho frustrante que um desenvolvedor que tenha que mudar alguma coisa tenha dificuldade em obter uma visão geral da arquitetura. Portanto, há uma tendência para corrigir problemas ou fazer alterações de maneira que funcione na arquitetura original. O resultado é um código que se torna cada vez mais complexo e ainda mais difícil de entender.

Existe algum conselho útil sobre como manter o código fonte realmente sustentável ao longo dos anos?


9
recomendo livros: 'Project Guia de Software Survival' por Steve McConnell, 'rápido desenvolvimento' por Steve McConnell, 'Refatoração' por Martin Fowler
Imran Omar Bukhsh

15
... e 'Código Limpo' do tio Bob;) (Robert C. Martin)
Gandalf

34
Essa pergunta não é algo que gerou várias décadas de leitura pesada e cursos inteiros nas universidades?
detly

17
@ Eric Yin - eu discordo dos comentários. Para mim, eles são cheiros de código e, em projetos de longo prazo, tendem a causar mais mal do que bem, porque inevitavelmente ficam desatualizados e se tornam enganosos.
JohnFx

8
@ Eric Yin: esforce-se por código de auto-documentação. Use comentários de intenção apenas onde eles melhoram a compreensão.
Mitch Wheat

Respostas:


138

A única solução real para evitar a podridão do código é codificar bem!

Como codificar bem é outra questão. É difícil o suficiente, mesmo se você é um excelente programador trabalhando sozinho. Em uma equipe heterogênea, torna-se muito mais difícil ainda. Em (sub) projetos terceirizados ... apenas ore.

As boas práticas usuais podem ajudar:

  1. Mantenha simples.
  2. Mantenha simples. Isso se aplica especialmente à arquitetura, o "quadro geral". Se os desenvolvedores estão tendo dificuldade para obter o retrato grande, eles estão indo para codificar contra ela. Portanto, simplifique a arquitetura para que todos os desenvolvedores a entendam. Se a arquitetura precisa ser menos que simples, os desenvolvedores devem ser treinados para entender essa arquitetura. Se não o internalizarem, não deverão codificá-lo.
  3. Procure baixo acoplamento e alta coesão . Certifique-se de que todos na equipe entendam essa idéia. Em um projeto que consiste em partes coesas e fracamente acopladas, se algumas delas se tornarem uma bagunça impossível de manter, você pode simplesmente desconectar e reescrever essa parte. É mais difícil ou quase impossível se o acoplamento estiver apertado.
  4. Ser consistente. Quais padrões seguir pouco importam, mas siga alguns padrões. Em uma equipe, todos devem seguir os mesmos padrões, é claro. Por outro lado, é fácil ficar muito apegado aos padrões e esquecer o resto: por favor, entenda que, embora os padrões sejam úteis, eles são apenas uma pequena parte da criação de um bom código. Não faça um grande número disso.
  5. As revisões de código podem ser úteis para que uma equipe trabalhe de forma consistente.
  6. Certifique-se de que todas as ferramentas - IDEs, compiladores, controle de versão, sistemas de construção, geradores de documentação, bibliotecas, computadores , cadeiras , ambiente geral etc. etc. - sejam bem mantidas para que os desenvolvedores não precisem perder tempo com problemas secundários, como como conflitos na versão do arquivo do projeto, atualizações do Windows, barulho e qualquer coisa banal, mas irritante. Ter que gastar repetidamente um tempo considerável com coisas tão desinteressantes reduz o moral, o que pelo menos não melhora a qualidade do código. Em uma equipe grande, pode haver um ou mais funcionários cujo trabalho principal é manter as ferramentas do desenvolvedor.
  7. Ao tomar decisões tecnológicas, pense no que seria necessário para mudar a tecnologia; quais decisões são irreversíveis e quais não são. Avalie as decisões irreversíveis com muito cuidado. Por exemplo, se você decidir escrever o projeto em Java , é uma decisão praticamente irreversível. Se você decidir usar algum formato binário auto-fervido para arquivos de dados, isso também será uma decisão bastante irreversível (quando o código estiver disponível e você precisar continuar apoiando esse formato). Porém, as cores da GUI podem ser facilmente ajustadas, os recursos inicialmente deixados de fora podem ser adicionados posteriormente, portanto, menos se preocupe com esses problemas.

8
Estes são ótimos pontos. Devo admitir que luto com "mantê-lo simples". Parece significar coisas diferentes para pessoas diferentes em contextos diferentes, o que torna o "simples" bastante complexo (mas, em seguida, tenho uma tendência natural de complicar as coisas).
Kramii

3
Eu concordo perfeitamente com seus pontos, especialmente "KIS". Mas vejo uma tendência de que mais e mais desenvolvedores (mais jovens?) Usem estruturas bastante complexas para descrever até os contextos mais simples.
chrmue


8
Umm e "8. Mantenha as coisas simples".
Dawood ibn Kareem

7
O número 6 é digno de um +10.
Jim G.

55

Os testes de unidade são seus amigos . Implementá-los força baixo acoplamento. Isso também significa que as partes "hacky" do programa podem ser facilmente identificadas e refatoradas. Isso também significa que quaisquer alterações podem ser testadas rapidamente para garantir que não quebrem a funcionalidade existente. Isso deve incentivar seus desenvolvedores a modificar os métodos existentes, em vez de duplicar o código por medo de quebrar as coisas.

Os testes de unidade também funcionam como uma documentação extra para o seu código, descrevendo o que cada parte deve fazer. Com testes de unidade extensivos, seus programadores não precisam conhecer toda a arquitetura do seu programa para fazer alterações e usar classes / métodos existentes.

Como um bom efeito colateral, esperamos que os testes de unidade reduzam a contagem de erros.


3
Ponto muito importante. Eu assumi um sistema legado, muitas classes, muitas linhas de código, nenhuma documentação, nenhum teste de unidade. Depois de criar diligentemente testes de unidade para todas as correções e aprimoramentos de código, o design do sistema evoluiu para um estado mais limpo e mais sustentável. E temos a "coragem" de reescrever partes importantes do núcleo (cobertas por testes de unidade).
Sam Goldberg

40

Todo mundo aqui é rápido em mencionar o apodrecimento do código , e eu entendo e concordo completamente com isso, mas ainda falta o quadro geral e o maior problema em questão aqui. O apodrecimento do código não acontece apenas. Além disso, são mencionados testes de unidade que são bons, mas na verdade não solucionam o problema. Pode-se ter uma boa cobertura de teste de unidade e um código relativamente livre de erros, no entanto, ainda possui código e design apodrecidos.

Você mencionou que o desenvolvedor que trabalha em um projeto tem dificuldade em implementar um recurso e perde a visão geral da arquitetura geral e, portanto, implementa um hack no sistema. Onde está a liderança técnica para aplicar e influenciar o design? Onde estão as revisões de código nesse processo?

Na verdade, você não está sofrendo de podridão de código, mas está sofrendo de podridão em equipe . O fato é que não deve importar se os criadores originais do software não estão mais na equipe. Se o líder técnico da equipe existente entender completamente e verdadeiramente o design subjacente e for bom no papel de líder técnico, isso não será um problema.


Muito bom ponto, você atingiu o alvo! É triste dizer, mas é exatamente isso que está acontecendo aqui. E isso parece ser impossível mudar as coisas sem a liderança tecnologia ...
chrmue

4
@chrmue Do jeito que as coisas correm, eu acho, mas estou ficando cansado disso. De muitas maneiras, eu gostaria de ser um desenvolvedor júnior novamente quando não estava tão ciente de quão errado tudo ao meu redor parece estar. Parece que estou atingindo minha crise no meio da carreira mais cedo. Mas estou divagando ... feliz em ajudar.
maple_shaft

1
@ Murph Por que você não deveria ter um líder de equipe onisciente durante a fase de manutenção? Todo chefe que eu já esperava nada menos do líder de uma equipe, e quando eu era líder da equipe, não esperava nada menos de mim.
maple_shaft

1
@ maple_shaft porque a) não presumo que exista um líder de equipe dedicado a esse projeto e esse seja mais ou menos o primeiro requisito eb) acho que você precisa entender o design e a implementação (tudo isso) e isso é Difícil. Por um lado, todos nós argumentamos que não devemos ter uma única fonte de todo o conhecimento em nossos projetos e, no entanto, aqui estamos dizendo que precisamos ter um? Isso não se soma?
Murph

2
@maple_shaft provavelmente eu sendo um programador velho rabugento (-: Mas há um problema em que muitas vezes é um "estilo" de implementação que precisa ser seguido profundamente em uma base de código existente - e que pode ser estranho ao codificador e ao lead (por muitos por razões do mundo real).
Murph

18

Existem várias coisas que podemos fazer:

Dê a uma pessoa a responsabilidade geral pela arquitetura. Ao escolher essa pessoa, garanta que ela tenha a visão e a habilidade para desenvolver e manter uma arquitetura, e que ela tenha a influência e autoridade para ajudar outros desenvolvedores a seguir a arquitetura. Essa pessoa deve ser um desenvolvedor experiente, de confiança da gerência e respeitado por seus colegas.

Crie uma cultura em que todos os desenvolvedores se apropriem da arquitetura. Todos os desenvolvedores precisam estar envolvidos no processo de desenvolvimento e manutenção da integridade da arquitetura.

Desenvolva um ambiente em que as decisões arquitetônicas sejam facilmente comunicadas. Incentive as pessoas a falar sobre design e arquitetura - não apenas no contexto do projeto atual, mas em geral também.

As melhores práticas de codificação facilitam a visualização da arquitetura - leve tempo para refatorar, comentar o código, desenvolver testes de unidade etc. Coisas como convenções de nomenclatura e práticas de codificação limpas podem ajudar muito na comunicação da arquitetura, como uma equipe que você precisa reserve um tempo para desenvolver e seguir seus próprios padrões.

Garanta que toda a documentação necessária seja clara, concisa, atualizada e acessível. Torne públicos os diagramas de arquitetura de alto e baixo nível (fixá-los na parede pode ajudar) e publicamente de manutenção.

Finalmente (como perfeccionista natural), preciso reconhecer que a integridade da arquitetura é uma aspiração digna, mas que pode haver coisas mais importantes - como formar uma equipe que possa trabalhar bem em conjunto e realmente enviar um produto em funcionamento.


1
+1, especialmente para "formar uma equipe que possa trabalhar bem em conjunto e realmente enviar um produto em funcionamento".
deworde

1
É uma boa idéia ter uma pessoa sendo a raiz responsável pela arquitetura. No entanto, você tem um "cheiro de equipe" se essa responsabilidade for usada com frequência: a equipe deve, naturalmente, chegar a confusões comuns, em vez de confiar em uma pessoa para fornecer as respostas. Por quê? O conhecimento total do projeto é sempre compartilhado. Fixá-lo em uma pessoa levará a problemas maiores no final: apenas sua visão é satisfeita, cortando efetivamente as asas do resto da equipe. Em vez disso, contrate as melhores pessoas e deixe-as trabalhar juntas.
22812

1
@casper: Exatamente. Você expressa o que eu tinha em mente melhor do que eu.
Kramii

18

A maneira como resolvo esse problema é cortá-lo na raiz:

Minha explicação será usar termos do Microsoft / .NET , mas será aplicável a qualquer plataforma / caixa de ferramentas:

  1. Use padrões para nomeação, codificação, checkins, fluxo de bugs, fluxo de processos - basicamente qualquer coisa.
  2. Não tenha medo de dizer adeus aos membros da equipe que não seguem os padrões. Alguns desenvolvedores simplesmente não podem trabalhar dentro de um conjunto definido de padrões e se tornarão inimigos da 5ª coluna no campo de batalha para manter a base de código limpa
  3. Não tenha medo de alocar membros da equipe menos qualificados para testes por longos períodos de tempo.
  4. Use todas as ferramentas do seu arsenal para evitar o check-in do código em decomposição: isso envolve ferramentas dedicadas, bem como testes de unidade pré-escritos que testam os arquivos de construção, arquivos de projeto, estrutura de diretórios etc.
  5. Em uma equipe de cerca de 5 a 8 membros, peça ao seu padrinho que refatore quase constantemente - limpando a bagunça que os outros deixam para trás. Mesmo se você encontrar os melhores especialistas no campo, ainda terá uma bagunça - é inevitável, mas pode ser restringido pela constante refatoração.
  6. Faça testes de unidade e os mantenha - NÃO dependa dos testes de unidade para manter o projeto limpo, eles não.
  7. Discuta tudo. Não tenha medo de gastar horas para discutir as coisas da equipe. Isso disseminará as informações e removerá uma das causas principais do código incorreto: confusão sobre tecnologias, objetivos, padrões etc.
  8. Tenha muito cuidado com os consultores que escrevem código: o código deles, quase por definição, será realmente uma merda.
  9. Faça revisões de preferência como a etapa do processo antes do check-in. Não tenha medo de reverter confirmações.
  10. Nunca use o princípio de abrir / fechar, a menos que no último estágio antes do lançamento: ele simplesmente faça com que o código em decomposição seja deixado para cheirar.
  11. Sempre que um problema é solucionado, reserve um tempo para entendê-lo ao máximo antes de implementar uma solução - a maioria das rotações de código vem da implementação de soluções para problemas não totalmente compreendidos.
  12. Use as tecnologias certas. Geralmente, eles vêm em conjuntos e são atualizados: é melhor depender de uma versão beta de uma estrutura da qual você terá suporte no futuro, do que depender de estruturas extremamente estáveis, mas obsoletas, que não são suportadas.
  13. Contrate as melhores pessoas.
  14. Demitir o resto - você não está executando um café.
  15. Se o gerenciamento não é dos melhores arquitetos e eles interferem no processo de tomada de decisão - encontre outro emprego.

1
Agora, o que você faria se tivesse que manter e aprimorar um aplicativo VB6 de pesadelo de 12 anos com centenas de formulários e classes enquanto trabalhava em uma reescrita em C # no 'tempo livre'?
precisa saber é o seguinte

7
Não concordo com o nº 3. O teste não deve ser visto como uma punição para os não treinados. De fato, isso deve ser feito por todos os desenvolvedores e contado da mesma forma que a codificação e o design! Se você tem o Sr. Sem Treinamento na equipe, tire-o da equipe para treinamento.
NWS

1
"Não concordo com o nº 3. Os testes não devem ser vistos como uma punição para os não treinados." - Não deveria e não é o que escrevi, deixe-me explicar: o teste é uma boa maneira de deixar as pessoas que você ainda não confiam para cometer mudanças para entrar no código. Os melhores testadores que encontrei são aqueles que aspiram a se tornar contribuidores de código e estão provando sua competência examinando o código, executando o programa e mostrando a capacidade de correlacionar suas descobertas no tempo de execução com o código-fonte. Não é um castigo - seu treinamento.
22812

1
"Discordo do número 10 - isso ajuda na qualidade do código, se feito corretamente" Isso é absolutamente falso na minha experiência de trabalho: o código bloqueado não pode ser refatorado, o que significa que ele permanecerá em seu estado atual até ser desbloqueado. Pode-se verificar que esse estado está funcionando em algum estágio, mas, posteriormente, essa verificação é um falso positivo: todo o código deve ser deixado em aberto para refatoração até o estágio anterior ao teste final do sistema.
22812

3
@casper: IMHO, O princípio aberto / fechado não deve ser entendido como "você não pode mudar a fonte", mas "projetar o código como se ele fosse congelado". Certifique-se de que é possível estender o código como necessário, sem exigir alterações nele. O resultado é inerentemente mais frouxamente acoplado e altamente coeso do que o código médio. Esse princípio também é crucial ao desenvolver uma biblioteca para uso de terceiros, uma vez que eles não podem simplesmente entrar e modificar seu código, portanto, é necessário que ele seja extensível adequadamente.
Kevin Cathcart

12

Limpe o código apodrecido refatorando, enquanto escreve testes de unidade. Pague (isso) a dívida de projeto em todo o código que você tocar, sempre que:

  • Desenvolver um novo recurso
  • Corrigir um problema

Acelere bastante seu ciclo de desenvolvimento de teste primeiro:

  • Refatoração para converter módulos de código em uma linguagem de script
  • Use máquinas de teste rápidas e baseadas na nuvem

Refatorar o código para usar baixo acoplamento (de unidades altamente coesas internamente):

  • Mais simples, (mais) funções (rotinas)
  • Módulos
  • Objetos (e classes ou protótipos)
  • Funções puras (sem efeitos colaterais)
  • Preferindo delegação, sobre herança
  • Camadas (com APIs)
  • Coleções de pequenos programas de uso único que podem operar juntos

O crescimento orgânico é bom; um grande projeto inicial é ruim.

Tenha um líder que tenha conhecimento sobre o design atual. Caso contrário, leia o código do projeto até ter conhecimento.

Leia livros de refatoração.


1
+1 Boa primeira resposta, maneira perfeita de se apresentar para nós!
precisa saber é

11

Resposta simples: você não pode .

É por isso que você deve procurar escrever software pequeno e simples . Não é fácil.

Isso só é possível se você pensar o suficiente sobre o seu problema aparentemente complexo para defini-lo da maneira mais simples e concisa possível.

A solução para problemas realmente grandes e complexos ainda pode ser resolvida com base em módulos pequenos e simples.

Em outras palavras, como outros apontaram, a simplicidade e o acoplamento flexível são os principais ingredientes.

Se isso não for possível ou possível, você provavelmente está pesquisando (problemas complexos sem soluções simples conhecidas ou nenhuma solução conhecida). Não espere que a pesquisa produza diretamente produtos de manutenção, não é para isso que serve a pesquisa.


9

Eu trabalho em uma base de código para um produto que está em desenvolvimento contínuo desde 1999, então, como você pode imaginar, já é bastante complexo. A maior fonte de hackers em nossa base de código é das inúmeras vezes em que tivemos que portá-la do ASP Classic para o ASP.NET , do ADO ao ADO.NET, de postbacks ao Ajax , alternando bibliotecas de interface do usuário, padrões de codificação etc.

Em suma, fizemos um trabalho razoável para manter a base de código sustentável. As principais coisas que fizemos e que contribuíram para isso são:

1) Refatoração constante - Se você precisar tocar em um pedaço de código hacky ou difícil de entender, espera-se que dedique um tempo extra para limpá-lo e terá a margem de manobra prevista para fazê-lo. Os testes de unidade tornam isso muito menos assustador, porque você pode testar contra regressões mais facilmente.

2) Mantenha um ambiente de desenvolvimento limpo - esteja atento sobre a exclusão de código que não é mais usado e não deixe cópias de backup / cópias de trabalho / código experimental no diretório do projeto.

3) Padrões de codificação consistentes para a vida útil do projeto - Vamos ser sinceros, nossos pontos de vista sobre os padrões de codificação evoluem com o tempo. Sugiro manter o padrão de codificação iniciado por toda a vida útil de um projeto, a menos que você tenha tempo para voltar e adaptar todo o código para estar em conformidade com o novo padrão. É ótimo que você tenha superado a notação húngara agora, mas aplique essa lição a novos projetos e não apenas mude o meio do caminho nesse novo projeto.


8

Desde que você marcou a pergunta com gerenciamento de projetos, tentei adicionar alguns pontos que não são de código :)

  • Planeje a rotatividade - suponha que toda a equipe de desenvolvimento tenha desaparecido quando chegar à fase de manutenção - nenhum desenvolvedor que se preze quer ficar preso mantendo o sistema para sempre. Comece a preparar os materiais de entrega assim que tiver tempo.

  • Consistência / uniformidade não pode ser estressada o suficiente. Isso desencorajará uma cultura de 'vá sozinho' e incentivará os novos desenvolvedores a perguntar, se eles estiverem em dúvida.

  • Mantenha o mainstream - tecnologias usadas, padrões e padrões de design - porque um novo desenvolvedor da equipe (em qualquer nível) terá mais chances de começar a funcionar rapidamente.

  • Documentação - especialmente arquitetura - por que as decisões foram tomadas e os padrões de codificação. Além disso, mantenha referências / notas / roteiros para documentar o domínio comercial - você ficaria surpreso com o quão difícil é para os negócios corporativos explicar o que eles fazem com um desenvolvedor sem experiência no domínio.

  • Estabeleça as regras claramente - não apenas para sua equipe de desenvolvimento atual, mas pense nos futuros desenvolvedores de manutenção. Se isso significa colocar um hiperlink para a documentação padrão relevante de design e codificação em todas as páginas, que assim seja.

  • Verifique se a arquitetura e, especialmente, as camadas de código são claramente demarcadas e separadas - isso potencialmente permitirá a substituição de camadas de código à medida que novas tecnologias surgirem, por exemplo, substituir uma interface do usuário de formulários da Web por uma interface de usuário do jQuery HTML5 etc. compre um ano mais ou menos de longevidade.


7

Uma propriedade do código altamente sustentável é a pureza da função .

Pureza significa que as funções devem retornar o mesmo resultado para os mesmos argumentos. Ou seja, eles não devem depender dos efeitos colaterais de outras funções. Além disso, é útil se eles próprios não tiverem efeitos colaterais.

É mais fácil testemunhar essa propriedade do que as propriedades de acoplamento / coesão. Você não precisa se esforçar para alcançá-lo, e eu pessoalmente o considero mais valioso.

Quando sua função é pura, seu tipo é uma documentação muito boa por si só. Além disso, escrever e ler documentação em termos de argumentos / valor de retorno é muito mais fácil do que mencionar algum estado global (possivelmente acessado por outros threads O_O).

Como um exemplo do uso extensivo de pureza para ajudar na manutenção, você pode ver o GHC . Trata-se de um grande projeto com cerca de 20 anos em que grandes refatorações estão sendo feitas e novos recursos importantes ainda estão sendo introduzidos.

Por fim, não gosto muito do ponto "Mantenha as coisas simples". Você não pode manter seu programa simples quando estiver modelando coisas complexas. Tente criar um compilador simples e seu código gerado provavelmente acabará lento. Claro, você pode (e deve) simplificar as funções individuais, mas o programa inteiro não será simples como resultado.


2
Outro nome para o que você descreve é a propriedade de ser determinístico
jinglesthula

6

Além das outras respostas, eu recomendaria camadas. Não são muitos, mas o suficiente para separar diferentes tipos de código.

Usamos um modelo de API interno para a maioria dos aplicativos. Há uma API interna que se conecta ao banco de dados. Em seguida, uma camada de interface do usuário . Pessoas diferentes podem trabalhar em cada nível sem interromper ou quebrar outras partes dos aplicativos.

Outra abordagem é fazer com que todos leiam comp.risks e The Daily WTF para que aprendam as consequências de um design ruim e de uma programação ruim, e eles terão medo de ver seu próprio código publicado no The Daily WTF .


6

Como muitas dessas respostas parecem se concentrar em equipes grandes, mesmo desde o início, vou colocar minha opinião como parte de uma equipe de desenvolvimento de dois homens (três se você incluir o designer) para uma startup.

Obviamente, projetos e soluções simples são os melhores, mas quando você tem o cara que literalmente paga seu salário pelo pescoço, não tem tempo para pensar na solução mais elegante, simples e sustentável. Com isso em mente, meu primeiro grande ponto é:

Documentação Não comentários, o código deve ser principalmente auto-documentado, mas coisas como documentos de design, hierarquias e dependências de classes, paradigmas arquitetônicos etc. Qualquer coisa que ajude um programador novo ou mesmo existente a entender a base de código. Além disso, documentar aquelas pseudo-bibliotecas ímpares que aparecem eventualmente, como "adicionar esta classe a um elemento para essa funcionalidade" pode ajudar, pois também impede que as pessoas reescrevam a funcionalidade.

No entanto, mesmo se você tiver um limite de tempo severo, acho que outra coisa boa a ter em mente é:

Evite hacks e correções rápidas. A menos que a solução rápida seja a solução real, é sempre melhor descobrir o problema subjacente para alguma coisa e, em seguida, corrigir isso. A menos que você tenha literalmente um cenário "faça isso funcionar nos próximos 2 minutos ou esteja demitido", fazer a correção agora é uma idéia melhor, porque você não corrigirá o código mais tarde, apenas passe para a próxima tarefa que você tem.

E minha dica favorita pessoal é mais uma citação, embora não me lembre da fonte:

"Codifique como se a pessoa que vem atrás de você é um psicopata homicida que sabe onde você mora"


Eu sempre achei os comentários de função e de classe úteis, mesmo que apenas para fornecer separação de segmentos de código nos locais de função e classe, usando o destaque de sintaxe. Eu raramente coloco comentários no código da função, mas escrevo uma linha para cada classe e função, como /** Gets the available times of a clinic practitioner on a specific date. **/or /** Represents a clinic practitioner. **/.
Nick Bedford

5

Um princípio que não foi mencionado, mas que considero importante, é o princípio aberto / fechado .

Você não deve modificar o código que foi desenvolvido e testado: qualquer parte desse código está selada. Em vez disso, estenda as classes existentes por meio de subclasses ou use-as para escrever wrappers, classes decoradoras ou usar qualquer padrão que achar adequado. Mas não altere o código de trabalho .

Apenas meus 2 centavos.


E se os requisitos de negócios expressos no código de trabalho mudarem? Um não toque no código mal fatorado, mas tecnicamente "funcional", pode prejudicá-lo a longo prazo, especialmente quando chega a hora de fazer as alterações necessárias.
Jinglesthula

@jinglesthula: Abrir para extensão significa que você pode adicionar a nova funcionalidade como uma nova implementação (por exemplo, classe) de uma interface existente. Obviamente, código mal estruturado não permite isso: deve haver uma abstração como uma interface que permita que o código "mude" adicionando novo código em vez de modificar o código existente.
Giorgio

5
  • Seja um olheiro . Sempre deixe o código mais limpo do que o encontrado.

  • Corrija as janelas quebradas . Todos esses comentários "mudam na versão 2.0" quando você está na versão 3.0.

  • Quando houver grandes invasões, projete uma solução melhor como equipe e faça-o. Se você não pode consertar o hack como uma equipe, não entende o sistema suficientemente bem. "Peça ajuda a um adulto." As pessoas mais velhas podem ter visto isso antes. Tente desenhar ou extrair um diagrama do sistema. Tente desenhar ou extrair os casos de uso particularmente invasivos como diagramas de interação. Isso não corrige, mas pelo menos você pode vê-lo.

  • Que suposições não são mais verdadeiras que impulsionaram o design em uma direção específica? Pode haver uma pequena refatoração escondida atrás de um pouco dessa bagunça.

  • Se você explicar como o sistema funciona (mesmo apenas um caso de uso) e tiver que se desculpar repetidamente de um subsistema, esse é o problema. O que o comportamento tornaria o resto do sistema mais simples (não importa o quão difícil pareça implementar em comparação com o que existe). O subsistema clássico a ser reescrito é aquele que polui todos os outros subsistemas com sua semântica e implementação operacionais. "Ah, você precisa alterar os valores antes de alimentá-los no subsistema froo e descompactá-los novamente à medida que obtém a saída do froo. Talvez todos os valores devam ser alterados quando lidos pelo usuário e armazenamento, e o resto do sistema está errado? Isso fica mais emocionante quando existem duas ou mais especificações diferentes.

  • Passe uma semana como uma equipe removendo avisos para que problemas reais sejam visíveis.

  • Reformate todo o código para o padrão de codificação.

  • Verifique se o seu sistema de controle de versão está vinculado ao seu rastreador de erros. Isso significa que mudanças futuras são agradáveis ​​e responsáveis, e você pode descobrir POR QUE.

  • Faça alguma arqueologia. Encontre os documentos de design originais e revise-os. Eles podem estar naquele PC antigo no canto do escritório, no espaço abandonado ou no arquivo que ninguém abre.

  • Republicar os documentos de design em um wiki. Isso ajuda a institucionalizar o conhecimento.

  • Escreva procedimentos do tipo lista de verificação para lançamentos e compilações. Isso impede as pessoas de pensarem, para que possam se concentrar na solução de problemas. Automatize compilações sempre que possível.

  • Experimente a integração contínua . Quanto mais cedo você obtiver uma compilação com falha, menos tempo o projeto poderá gastar fora dos trilhos.

  • Se o líder da sua equipe não faz essas coisas, isso é ruim para a empresa.

  • Tente garantir que todo o novo código obtenha testes de unidade adequados com cobertura medida. Portanto, o problema não pode ficar muito pior.

  • Tente testar alguns dos bits antigos que não foram testados. Isso ajuda a reduzir o medo da mudança.

  • Automatize seu teste de integração e regressão, se puder. Pelo menos tenha uma lista de verificação. Os pilotos são inteligentes, recebem lotes pagos e usam listas de verificação. Eles também estragam muito raramente.


4

Leia e, em seguida, releia o Code Complete de Steve McConnell. É como uma bíblia de boa escrita de software, desde o design inicial do projeto até uma única linha de código e tudo mais. O que mais gosto sobre isso é o backup de décadas de dados sólidos; não é apenas o próximo melhor estilo de codificação.


3

Eu vim para chamar isso de "Efeito Winchester Mystery House". Como a casa, começou bastante simples, mas, ao longo dos anos, muitos trabalhadores diferentes adicionaram tantos recursos estranhos sem um plano geral que ninguém mais o entende mais. Por que essa escada não leva a lugar nenhum e por que essa porta se abre apenas de um jeito? Quem sabe?

A maneira de limitar esse efeito é começar com um bom design feito de forma flexível o suficiente para lidar com a expansão. Várias sugestões já foram oferecidas sobre isso.

Porém, muitas vezes você aceita um trabalho onde o dano já foi causado e é tarde demais para um bom design sem executar uma reformulação e reescrita cara e potencialmente arriscada. Nessas situações, é melhor tentar encontrar maneiras de limitar o caos enquanto o adota em algum grau. Pode incomodar as sensibilidades de seu projeto de que tudo tem que passar por uma classe enorme e feia de 'gerenciadores' ou a camada de acesso a dados está fortemente acoplada à interface do usuário, mas aprenda a lidar com isso. Codifique defensivamente dentro dessa estrutura e tente esperar o inesperado quando aparecerem os 'fantasmas' dos programadores.


2

A refatoração de código e o teste de unidade estão perfeitamente corretos. Mas, como esse projeto de longa duração está sendo invadido por hackers, isso significa que a gerência não está se esforçando para limpar a podridão. É necessário que a equipe introduza hacks, porque alguém não está alocando recursos suficientes para treinar pessoas e analisar o problema / solicitação.

Manter um projeto de longo prazo é tanto uma responsabilidade do gerente de projeto quanto um desenvolvedor individual.

As pessoas não introduzem hacks porque gostam; eles são forçados pelas circunstâncias.


Forçado pelas circunstâncias? As pessoas introduzem hacks porque (a) não sabem melhor => precisam de treinamento, (b) não veem o quadro geral => comunicação, documentação e disciplina necessárias, (c) acham que são mais inteligentes => essa é a maior obstáculo a superar (d) forçado pelas circunstâncias => hotfixes rápidos são aceitáveis ​​quando estão sob pressão do tempo, mas alguém precisa assumir a responsabilidade e limpar o código posteriormente. Qualquer outra "circunstância" é simplesmente BS . Existem exceções a essa regra geral, mas as exceções mais conhecidas são "preguiçosas".
JensG

2

Eu só quero colocar uma questão não técnica e uma abordagem (talvez) pragmática.

Se o seu gerente não se importa com a qualidade técnica (código gerenciável, arquitetura simples, infraestrutura confiável e assim por diante), fica difícil melhorar o projeto. Nesse caso, é necessário educar o gerente e convencê-lo a "investir" os esforços na manutenção e no endividamento técnico .

Se você sonha com a qualidade do código encontrada nesses livros, também precisa de um chefe preocupado com isso.

Ou se você quiser domesticar um "projeto Frankenstein", estas são as minhas dicas:

  • Organize e simplifique
  • Encurtar funções
  • Priorize a legibilidade sobre a eficiência (quando aceitável, é claro, e alguns ganhos de eficiência são muito difíceis de serem mantidos)

Na minha experiência, a programação é entrópica e não emergente (pelo menos no paradigma estruturado em imperativos populares). Quando as pessoas escrevem código para "simplesmente trabalhar", a tendência é perder a organização. Agora, a organização do código requer tempo, às vezes muito mais do que fazê-lo funcionar.

Além da implementação de recursos e correções de bugs, reserve um tempo para a limpeza do código.


"... o projeto está condenado" - na minha experiência, isso não é necessariamente verdade. Alternativa é educar disse o gerente e convencer a "investir" esforços para manutenção e abordando técnico-dívida
mosquito

Desculpe, não consegui escrever isso, pois já tinha uma experiência em que o gerente ignorou todos os meus conselhos de dívida técnica. Mas acho que você está certo: cerca de um ano depois de desistir desse projeto, o gerente perdeu toda a sua autoridade sobre a equipe de tecnologia por sua incapacidade de gerenciá-los.
precisa saber é o seguinte

1

Fiquei surpreso ao descobrir que nenhuma das numerosas respostas destacava o óbvio: faça o software consistir em inúmeras bibliotecas pequenas e independentes. Com muitas bibliotecas pequenas, você pode construir um software grande e complexo. Se os requisitos forem alterados, você não precisará descartar toda a base de códigos ou investigar como modificar uma grande base de códigos de buzinas para fazer algo diferente do que está fazendo atualmente. Você apenas decide quais dessas bibliotecas ainda são relevantes após a alteração dos requisitos e como combiná-las para obter a nova funcionalidade.

Use qualquer técnica de programação nessas bibliotecas que facilite o uso da biblioteca. Observe que, por exemplo, qualquer ponteiro de função que suporte a linguagem não orientada a objetos está suportando realmente programação orientada a objetos (OOP). Então, por exemplo, em C, você pode fazer OOP.

Você pode até considerar compartilhar essas bibliotecas pequenas e independentes entre muitos projetos (os submódulos git são seus amigos).

Escusado será dizer que cada pequena biblioteca independente deve ser testada em unidade. Se uma biblioteca específica não pode ser testada por unidade, você está fazendo algo errado.

Se você usa C ou C ++ e não gosta da idéia de ter muitos arquivos .so pequenos, é possível vincular todas as bibliotecas em um arquivo .so maior ou, alternativamente, pode vincular estática. O mesmo vale para Java, basta alterar .so para .jar.


Isso parece ser um pouco teórico do meu ponto de vista. É claro que os projetos que mencionei na minha pergunta foram construídos com várias bibliotecas e módulos. A minha experiência nos últimos 26 anos de desenvolvimento de software era que quanto mais velho o projeto se torna a organização inicial
chrmue

0

Simples: reduza os custos de manutenção da maior parte do seu código para zero até que você tenha um número sustentável de peças móveis. O código que nunca precisa ser alterado não incorre em custos de manutenção. Eu recomendo que o objetivo de tornar o código realmente tenha custo zero de manutenção, não tentando reduzir o custo em muitas iterações de refatoração pequenas e exigentes. Faça com que o custo seja zero imediatamente.

Ok, é verdade que é muito, muito mais difícil do que parece. Mas não é difícil começar. Você pode pegar uma parte da base de código, testá-la, construir uma interface agradável sobre ela se o design da interface estiver uma bagunça e começar a aumentar as partes da base de código que são confiáveis, estáveis ​​(como na falta de motivos para mudar), enquanto simultaneamente encolhendo as partes que não são confiáveis ​​e instáveis. As bases de código que parecem um pesadelo para manter geralmente não distinguem as partes móveis que precisam ser alteradas das partes que não o fazem, pois tudo é considerado não confiável e propenso a mudanças.

Na verdade, recomendo separar a organização da sua base de código em partes "estáveis" e "instáveis", com as partes estáveis ​​sendo uma enorme PITA para reconstruir e mudar (o que é uma coisa boa, pois elas não precisam alterados e reconstruídos se realmente pertencerem à seção "estável").

Não é do tamanho de uma base de código que dificulta a manutenção. É o tamanho da base de código que precisa ser mantida. Eu dependo de milhões de linhas de código sempre que, digamos, uso a API do sistema operacional. Mas isso não contribui para os custos de manutenção do meu produto, pois não preciso manter o código fonte do sistema operacional. Eu apenas uso o código e funciona. O código que eu apenas uso e nunca preciso manter não incorre em custos de manutenção do meu lado.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.