Ser estúpido para obter melhor produtividade?

110

Eu passei muito tempo lendo livros diferentes sobre "bom design", "padrões de design", etc. Sou um grande fã do SOLID abordagem e toda vez que eu preciso escrever um simples código, eu penso no futuro. Então, se implementar um novo recurso ou uma correção de bug requer apenas adicionar três linhas de código como esta:

if(xxx) {
    doSomething();
}

Isso não significa que eu farei assim. Se eu achar que este pedaço de código provavelmente ficará maior no futuro próximo, vou pensar em adicionar abstrações, mover essa funcionalidade para outro lugar e assim por diante. O objetivo que estou perseguindo é manter a média complexidade da mesma forma que era antes das minhas alterações.

Acredito que, do ponto de vista do código, é uma boa ideia - meu código nunca é longo o suficiente e é muito fácil entender os significados de diferentes entidades, como classes, métodos e relações entre classes e objetos. / p>

O problema é que leva muito tempo, e muitas vezes sinto que seria melhor se eu implementasse esse recurso "como está". É apenas sobre "três linhas de código" versus "nova interface + duas classes para implementar essa interface".

Do ponto de vista do produto (quando estamos falando sobre o resultado ), as coisas que faço são completamente sem sentido. Eu sei que, se vamos trabalhar na próxima versão, ter um bom código é realmente ótimo. Mas do outro lado, o tempo que você gastou para tornar seu código "bom" pode ter sido gasto para implementar alguns recursos úteis.

Muitas vezes me sinto muito insatisfeito com meus resultados - um código bom que só pode fazer A é pior do que um código ruim que pode fazer A, B, C e D.

Essa abordagem provavelmente resultará em um ganho líquido positivo para um projeto de software ou será uma perda de tempo?

    
por loki2302 28.07.2017 / 15:36
fonte

17 respostas

152

good code that only can do A is worse than bad code that can do A, B, C, D.

Isso cheira a mim como generalidade especulativa . Sem saber (ou pelo menos estar razoavelmente seguro) que seus clientes precisarão dos recursos B, C e D, você está apenas complicando desnecessariamente o seu design. Um código mais complexo é mais difícil de entender e manter a longo prazo. A complexidade extra é justificada apenas por recursos extras úteis . Mas normalmente somos muito ruins em prever o futuro. A maioria dos recursos que achamos que podem ser necessários no futuro nunca será solicitada na vida real.

Então:

Bom código que só pode fazer A (mas está fazendo isso de uma maneira simples e clara) é MELHOR do que código ruim que pode fazer A, B, C, D (alguns dos quais podem

    
por 06.09.2011 / 13:22
fonte
87

Tempo de anedota:

Eu tive dois desenvolvedores trabalhando para mim, que se inclinaram para o excesso de engenharia dessa maneira.

Para um deles, isso praticamente paralisou sua produtividade, especialmente ao iniciar um novo projeto. Mais especialmente se o projeto era, por sua natureza, bastante simples. Em última análise, um software que funciona agora é o que precisamos. Isso ficou tão ruim que eu tive que deixá-lo ir.

O outro desenvolvedor que era propenso a engenharia excessiva compensou isso por ser extremamente produtivo. Como tal, apesar de todo o código estranho, ele ainda entregou mais rápido que a maioria. No entanto, agora que ele se mudou, muitas vezes me sinto irritado com a quantidade de trabalho extra que é necessário para adicionar funcionalidade, já que você tem que modificar (totalmente desnecessárias) camadas de abstração, etc.

Portanto, a moral é que a engenharia excessiva consumirá tempo extra que poderia ser melhor gasto fazendo coisas úteis. E não apenas o seu próprio tempo, mas também daqueles que têm que trabalhar com o seu código.

Então não faça isso.

Você quer que seu código seja o mais simples possível (e não mais simples). Lidar com 'maybes' não está tornando as coisas mais simples, se você errar, você terá tornado o código mais complexo sem nenhum ganho real para mostrar isso.

    
por 06.09.2011 / 12:55
fonte
26

Os princípios SOLID e KISS / YAGNI são opostos próximos do polar. Alguém lhe dirá que se doSomething () não puder ser justificado como parte integrante do job que a classe que está chamando, ele deve estar em uma classe diferente que é fracamente acoplada e injetada. O outro lhe dirá que, se este é o único lugar em que doSomething é usado, até mesmo extraí-lo em um método pode ter sido um exagero.

Isto é o que faz com que bons programadores valham seu peso em ouro. A estrutura "adequada" é uma base caso a caso, exigindo conhecimento da base de código atual, do caminho futuro do programa e das necessidades do negócio por trás do programa.

Eu gosto de seguir esta metodologia simples de três etapas.

  • Na primeira passagem, faça funcionar.
  • Na segunda passagem, limpe-a.
  • Na terceira passagem, torne-a SOLID.

Basicamente, é assim que você mistura o KISS com o SOLID. Quando você escreve uma linha de código pela primeira vez, por tudo o que você sabe, será uma única vez; simplesmente tem que trabalhar e ninguém se importa como, então não fique chique. Na segunda vez que você coloca o cursor nessa linha de código, você refutou sua hipótese original; Ao revisitar esse código, é provável que você o estenda ou o conecte de algum outro lugar. Agora, você deve limpá-lo um pouco; extraia métodos para informações mais usadas, reduza ou elimine a codificação de copiar / colar, adicione alguns comentários, etc. Na terceira vez que você voltar a esse código, agora é uma grande interseção para os caminhos de execução do seu programa. Agora você deve tratá-lo como parte essencial de seu programa e aplicar as regras do SOLID sempre que possível.

Exemplo: você precisa escrever uma linha simples de texto no console. A primeira vez que isso acontece, o Console.WriteLine () é bom. Em seguida, você retorna a esse código depois que novos requisitos também determinam a gravação da mesma linha em um log de saída. Neste exemplo simples, pode não haver muito código "copiar / colar" repetitivo (ou talvez exista), mas ainda é possível fazer algumas limpezas básicas, talvez extrair um método ou dois para evitar a inclusão de operações de E / S em lógica de negócios mais profunda . Então, você volta quando o cliente quer a mesma saída de texto para um pipe nomeado para um servidor de monitoramento. Agora, essa rotina de saída é um grande negócio; você está transmitindo o mesmo texto de três maneiras. Este é o exemplo de livro didático de um algoritmo que se beneficiaria de um padrão Composite; desenvolver uma interface IWriter simples com um método Write (), implementar essa interface para criar classes ConsoleWriter, FileWriter e NamedPipeWriter, e mais uma vez criar uma classe composta "MultiWriter", expor uma dependência IWriter em sua classe, configurar o MultiWriter compósito com os três escritores reais, e ligá-lo. Agora, é bastante sólido; a partir daí, sempre que os requisitos determinarem que a saída deve ir a qualquer lugar novo, basta criar um novo IWriter e conectá-lo ao MultiWriter, sem necessidade de tocar em nenhum código de trabalho existente.

    
por 06.09.2011 / 17:46
fonte
17

good code that only can do A is worse than bad code that can do A, B, C, D.

1) Tenha um código que faça apenas o que é suposto fazer.

If I feel like this piece of code is likely to become larger in the nearest future, I'll think of adding abstractions, moving this functionality somewhere else and so on.

2) Se você planeja seu código para fazer A, B, C e D, o cliente irá lhe pedir E

Seu código deve fazer o que é suposto fazer, você não deve pensar agora em futuras implementações, porque você nunca terminará mudando seu código continuamente, e mais importante você projetará seu código. Você deve refatorar seu código assim que sentir que precisa dele por causa dos recursos atuais, sem se esforçar para prepará-lo para algo que ainda não será feito, a menos que você planeje isso como parte da arquitetura do projeto.

Sugiro que você leia um bom livro: O programador pragmático. Isso abrirá sua mente e ensinará o que você deve fazer e o que você não deve fazer.

Além disso, o Código Completo é um ótimo recurso cheio de informações úteis que todo desenvolvedor (não apenas programador) deve ler.

    
por 06.09.2011 / 20:21
fonte
12

very time I need to write a simple piece of code, I think about future

Talvez aqui esteja o problema.

Nos estágios iniciais, você não tem idéia do que seria o produto final. Ou se você tiver, está errado. Com certeza. É como um garoto de 14 anos que perguntou há alguns dias em Programmers.SE se ele deve, para sua futura carreira, escolher entre aplicativos da web e eu não lembro mais o quê: é bem óbvio que em poucos anos, as coisas ele gosta vai mudar, ele vai se interessar por outros domínios, etc.

Se, para escrever três linhas de código, você criar uma nova interface e duas classes, estará super-engenharia. Você obterá um código que será difícil de manter e difícil de ler , pois, para cada linha de código útil, você tem duas linhas de código desnecessárias. Sem contar a documentação XML, testes de unidade, etc.

Pense nisso: se eu quiser saber como um recurso é implementado em seu código, seria mais fácil para eu ler vinte linhas de código, ou seria mais rápido e mais fácil ter que abrir dezenas de classes vazias e interfaces para descobrir qual delas usa quais, como elas estão relacionadas, etc.?

Lembre-se: uma base de código maior significa mais manutenção. Não escreva mais código quando puder evitá-lo.

Sua abordagem também é prejudicial em outros lados:

  • Se você precisar remover um recurso, não é mais fácil descobrir onde um método específico de vinte linhas é usado, do que desperdiçar seu tempo para entender as dependências entre dezenas de classes?

  • Ao depurar, não é mais fácil ter um pequeno rastreamento de pilha, ou você prefere ler dezenas de linhas para descobrir o que está sendo chamado por quê?

Para concluir, parece semelhante a otimização prematura . Você está tentando resolver o problema sem ter certeza se há um problema em primeiro lugar e onde está. Ao trabalhar na versão 1 do seu produto, implemente os recursos que você precisa implementar agora; não pense em algo que você espera implementar em dois anos na versão 14.

    
por 06.09.2011 / 12:33
fonte
5

Escrever um monte de código que (provavelmente) nunca será usado é uma ótima maneira de obter um P45 . Você não tem uma bola de cristal e não tem idéia da direção final que o desenvolvimento levará para gastar tempo nessas funções apenas custa dinheiro sem retorno.

    
por 06.09.2011 / 12:29
fonte
3

Tentando prever o que pode ser necessário a partir do código no futuro, muitas vezes acaba sendo desnecessariamente over-engineering (um hábito que estou tentando agitar no momento). Eu diria apenas fazer as três linhas. Quando surge a necessidade (e não antes), refatorar. Dessa forma, seu código sempre faz o que precisa sem ser mais complicado e evolui naturalmente com boa estrutura por meio de refatoração.

    
por 06.09.2011 / 12:25
fonte
3

Costumo dizer que a codificação é como o lado claro / lado escuro da Força - o lado "leve" requer mais esforço, mas produz maiores resultados. O lado "escuro" é rápido e fácil e dá maiores benefícios imediatamente, mas te corrompe. Uma vez que você comece o caminho sombrio, para sempre ele dominará o seu destino.

Eu me deparo com isso o tempo todo, em todos os trabalhos que já tive, é como uma maldição da qual não posso escapar. A cultura da empresa é sempre o caminho do lado negro, e hacks / consertos rápidos para empurrar novos recursos, e meus apelos e gritos de refatoração e escrita de código apropriadamente entram em ouvidos surdos, se não o fizerem t levar ao meu término por "tentar mudar as coisas" (nenhuma piada que eu tenha tido que acontecer um punhado de vezes, porque eu queria introduzir padrões de design e me afastar de hacks rápidos).

A triste verdade é que muitas vezes o caminho do lado estúpido / escuro é o caminho que você tem que pisar, você só tem que ter certeza de pisar levemente. Eu percebi lenta e tristemente que os programadores que entendem o modo correto de escrever software, ou seja, seguir SOLID, usando padrões de design, obedecendo ao SoC, etc. são muito menos comuns do que hacks sem noção que escreverão um if statement para corrigir um bug, e quando surgem mais bugs, basta adicionar a essa instrução em vez de pensar "Talvez exista uma maneira melhor de fazer isso" e refatorar o código para ser adequadamente extensível.

    
por 06.09.2011 / 14:25
fonte
3

Estar ciente do que pode acontecer (futuro) NUNCA é ruim. Pensar sobre o que pode ser uma maneira melhor de fazer algo é parte do que faz você ser bom em seu trabalho. A parte mais difícil é determinar se o tempo gasto: a taxa de pagamento é justificada. Todos nós já vimos situações em que as pessoas fazem o "fácil" para parar o sangramento imediato (e / ou gritar), e que, à medida que elas se acumulam, você fica confuso. Muitos de nós também já experimentamos uma abstração exagerada que é um mistério quando o codificador original se move, o que também produz um código confuso.

Eu olharia para sua situação e faria estas perguntas:

  1. Esse material de código é de missão crítica e será significativamente mais estável se eu recodificar? Na cirurgia falar, é isto refatoração de salva-vidas, ou é meramente eletivo e cosmético?

  2. Estou pensando em refatorar o código que vamos substituir em 6 meses?

  3. Estou disposto a gastar tanto tempo para documentar o design e meu raciocínio para futuros desenvolvedores como eu estou gastando fazendo o refatoração?

  4. Em relação ao meu design elegante para adicionar recursos futuros, é isso em código que os usuários solicitam alterações a cada semana, ou este é o primeiro vez que eu toquei este ano?

Há momentos em que YAGNI e KISS ganham o dia, mas há dias em que uma mudança fundamental fará com que você saia da espiral descendente para a maluquice. Contanto que você seja realista em sua avaliação não apenas do que deseja, mas do que os outros terão que fazer para manter seu trabalho, você será mais capaz de determinar qual situação é qual. Ah, e não se esqueça de escrever o que você fez e por quê. Vai salvar aqueles que te seguem, mas também você mesmo quando tiver que voltar mais tarde.

    
por 06.09.2011 / 15:29
fonte
3

Na segunda edição da linguagem de programação C ++ do Stroustrups, (eu não tenho a página disponível) eu li

Don't add code spontaneously in place.

e eu fui bem seguindo o conselho. Claro que existem compensações, e você tem que encontrar um equilíbrio, mas fragmentos curtos são mais testáveis do que uma grande bagunça de espaguete.

Eu frequentemente experimentei que, diferentemente de um caso para dois casos, se você pensa em 2 como n-casos, você abre uma porta para muitas novas possibilidades, sobre as quais você pode não ter pensado.

Mas então você tem que perguntar a pergunta YAGNI: Vale a pena? Será realmente útil? Ser experiente significa que você raramente estará errado e, como iniciante, com mais frequência errado.

Você deve ser crítico o suficiente para reconhecer um padrão e detectar se seu código é difícil de manter, por causa de muita abstração ou difícil de manter, porque tudo está resolvido no lugar.

A solução não é isto ou aquilo, mas pensar sobre isso. :)

    
por 06.09.2011 / 17:08
fonte
2

"código bom que só pode fazer A é pior que código ruim que pode fazer A, B, C e D."

Isso pode fazer algum sentido no desenvolvimento de produtos; Mas a maioria dos profissionais de TI trabalha em 'projetos' em vez de desenvolver produtos.

Em 'projetos de TI', se você programar um bom componenet, ele funcionará sem problemas durante a vida útil do projeto - que pode não durar mais de 5 ou 10 anos até que o cenário de negócios seja obsoleto e um novo produto do projeto / ERP pode tê-lo substituído. Durante este período de 5/10 anos de vida, a menos que existam defeitos em seu código, ninguém notará sua existência e os méritos de seus melhores pensamentos passarão despercebidos! (a menos que você seja bom em apostar seu próprio trompete alto!)

Não são muitos os que têm a oportunidade de programar o 'Windows Ctl + Alt + Del' e os poucos que têm essa chance podem não perceber o potencial futuro de seu código!

    
por 25.11.2011 / 10:09
fonte
1

Muitos livros sobre desenvolvimento enxuto e / ou ágil ajudarão a reforçar essa abordagem: faça o que for necessário no momento. Se você sabe que está construindo uma estrutura, adicione abstrações. Caso contrário, não adicione complexidade até que você precise. Eu recomendo o Lean Software Development , que introduzirá muitos outros conceitos que podem fazer uma diferença substantiva na produtividade.

    
por 06.09.2011 / 15:45
fonte
1

É engraçado como as pessoas falam sobre o jeito certo / errado de fazer as coisas. Ao mesmo tempo, a tarefa de programação ainda é dolorosamente difícil e não oferece boas soluções para escrever grandes sistemas complexos.

Pode ser que um dia nós, programadores, finalmente descubramos como escrever softwares complexos. Até lá, sugiro que você sempre comece com a implementação de protótipos "estúpidos" primeiro e depois gaste tempo suficiente na refatoração para que suas faculdades possam seguir seu código.

    
por 06.09.2011 / 16:04
fonte
1

Tendo visto designs tão prematuramente generalizados que não se encaixavam nos requisitos reais que vieram depois, criei uma regra para mim:

Para requisitos hipotéticos, apenas escreva um código hipotético.

Ou seja: é aconselhável pensar em alterações que possam ocorrer mais tarde. Mas use somente esses insights para escolher um design para o código que possa ser facilmente alterado e refatorado se esses requisitos realmente surgirem. Você pode até escrever algum código em sua cabeça (código hipotético) que você gostaria de escrever nesse caso, mas não escreva nenhum código real!

    
por 12.09.2011 / 22:08
fonte
0

Acho que a mentalidade que irá ajudá-lo é sempre buscar soluções concretas para problemas de codificação em vez de soluções abstratas. Abstrações só devem ser adicionadas quando elas realmente ajudarem a simplificar a base de código (quando, por exemplo, elas permitem que você torne a base de código menor).

Muitos programadores descobriram que as abstrações emergem quase sozinhas, quando SEQUEM seu código. Os Padrões de Design e as Melhores Práticas ajudam você a encontrar oportunidades para fazer exatamente isso, mas não são objetivos que valem a pena perseguir.

    
por 06.09.2011 / 16:32
fonte
0

Eu acho que o excesso de engenharia muitas vezes parece inseguro quanto a escrever código. Todos os princípios e padrões abstratos devem ser tratados como ferramentas para ajudá-lo. O que acontece frequentemente é que eles são tratados como padrões, um deve estar em conformidade com.

Eu acredito que um programador está sempre em uma posição melhor para decidir como abstrair do que um axioma.

O resto já foi dito por KeithS

    
por 08.09.2011 / 00:14
fonte
0

Pergunte-se quais são as vantagens de um bom design:

  • Mais fácil de entender
  • Mais fácil mantém
  • Portátil
  • Permanece útil por um longo tempo
  • Fácil de adicionar novos recursos

Agora, pergunte a si mesmo se adicionar todas essas camadas de abstrações realmente adicionam a qualquer um dos pontos mencionados acima. Se isso não acontecer, você está fazendo isso WRONG .

Se você conseguir adicionar novos recursos adicionando 3 linhas de código como esta:

if (condition()) {
  doSomething()
}

Então por favor, por favor faça isso. Isso indica que seu design anterior era bom e fácil de adaptar. Apenas quando suas aulas começarem a crescer além de uma certa extensão, use refatorando para dividir funções e possivelmente extrair novas classes.

Minha regra geral é que os novos recursos devem ser implementados da forma mais minimalista possível, somente quando algo é grande para ser compreendido (digamos, leva mais de 1 dia ou meio dia para ser implementado). desenhar. A partir daí, adicione apenas camadas de abstração quando o tamanho do código aumentar. Você então refatorou depois! Depois de um tempo, deve ser natural para você quando você precisa projetar um pedaço um pouco mais, ou ir para o caminho mais rápido. Outra indicação de que algum código pode usar alguma limpeza é quando você a reutiliza. Toda vez que você adicionar um novo recurso ou chamar um código antigo em um novo local, é uma boa hora para ver o código antigo e ver se você pode melhorá-lo um pouco antes de adicioná-lo novamente. Desta forma, o código quente irá lentamente tornar-se mais limpo, enquanto as partes desinteressantes apodrecem lentamente e não tiram o seu valioso tempo. (Se você usa algum código antigo e não consegue entendê-lo rapidamente, isso pode ser uma desmotivação para melhorá-lo, mas lembre-se de que também é um grande aviso que alguma limpeza / reprojeto é apropriada).

Se você trabalha assim, nunca superdimensionará nada. Pode ser preciso um pouco de disciplina para voltar ao código antigo quando você quiser adicionar algo novo ou deixar um novo código um pouco mais feio do que gostaria, mas você estará trabalhando em prol de algo produtivo em vez de engenharia excessiva.

    
por 28.07.2017 / 15:36
fonte