Código inútil na sua fonte


34

Eu ouvi histórias sobre isso de programadores seniores e já vi algumas delas. Parece que existem mais do que algumas instâncias de programadores escrevendo código inútil. Vou ver coisas como:

  • Chamadas de método ou função que não fazem nada de valor.
  • Verificações redundantes feitas em um arquivo de classe, objeto ou método separado.
  • if declarações que sempre são avaliadas como verdadeiras.
  • Tópicos que se desdobram e não fazem nada de importante.

Só para citar alguns. Disseram-me que isso ocorre porque os programadores desejam intencionalmente tornar o código confuso para aumentar seu próprio valor para a organização ou garantir negócios repetidos no caso de trabalho contratual ou terceirizado.

Minha pergunta é. Alguém mais viu código assim? Qual foi sua conclusão sobre por que esse código estava lá?

Se alguém escreveu um código como esse, você pode compartilhar o porquê?


4
if (false) {...}blocos são ótimos para comentar o código! </sarcasm>
dlras2

18
Nunca atribua à malícia aquilo que é adequadamente explicado pela estupidez , especialmente no desenvolvimento de software onde os hacks rápidos temporários raramente são temporários.
wildpeaks

1
@ dlras2 twist twist: # DEFINE false true :) #
Silviu Burcea

Respostas:


17

Ouvi desenvolvedores que tentam fazer com que suas conquistas de codificação soem mais complexas do que realmente são. Nunca ouvi alguém admitir isso, mas vi códigos que atendem aos seus critérios criados intencionalmente por pressa ou práticas inadequadas e não por sabotagem. O código ao redor do código mal-intencionado pode ter sido alterado até o ponto em que uma função específica não é mais útil.

Alguém realmente teria que ver esse código em primeira mão antes de chegar à conclusão de que somente esse desenvolvedor pode gerenciar a complexidade. A maioria dos gerentes e outras pessoas de negócios chega a essa conclusão porque não entende nenhum tipo de código e não deseja refazer a posição.


2
Estou inclinado a dar a resposta correta neste caso, porque parte do código que vejo simplesmente não pode ser não intencional ... a menos que alguém estivesse chapado quando codificava e pensasse que seria engraçado! Acredito que outras pessoas também tenham motivos relevantes para o código inútil, mas o código que vejo estão em projetos nos quais algumas pessoas trabalharam e eu sou o primeiro a fazer parte da equipe de desenvolvimento original. Devo dizer que parece um caso de complexidade adicionado a choque e pavor.
Ali

18
@ Ali: Nunca atribua à malícia o que é melhor explicado pela incompetência. Em outras palavras - o código provavelmente evoluiu para esse tipo de confusão, porque ninguém foi corajoso o suficiente para gastar tempo para realmente olhar para ele e ver o que ele realmente faz. Tudo isso soa como um monte de correções rápidas aplicadas, repetidas vezes, até que tudo o que resta é um monte de eca.
quickly_now

1
+1 para @quickly_now. Isso é geralmente o que acaba acontecendo; todo mundo tem medo de tocar em qualquer coisa que "funcione" por medo de quebrá-lo (ou, Deus o livre, demorar mais tempo em uma tarefa para realmente melhorar o código! O horror!). Assim, o código apodrece e fere e finalmente entra em colapso muitos anos depois.
Wayne Molina

@ Ali, houve casos em que o código que parece o mais semântico e razoável foi descrito como eu provavelmente pensando que isso é engraçado ou exibido. E vice-versa comigo vendo o código de outras pessoas. Você nunca sabe quem é louco, e na maioria das vezes tudo se resume a experiências e preferências. (sem falar de código objetivamente ruim aqui, apenas que essas descrições são facilmente contornadas)
Mihail Malostanidis 30/01

73

Não vi o código como este, mas vi o código que parece inútil ou inútil pelos outros motivos:

  1. Compatibilidade com versões anteriores. Você encontrou uma maneira muito melhor de fazer as coisas, mas deve manter a API / função antiga (e até agora não muito útil) porque algum módulo de terceiros por aí pode estar usando essa API / função para alguma coisa. Mesmo que a função não faça nada de útil, a ausência dela pode quebrar algum código.

  2. Codificação defensiva. Você sabe que as verificações neste código são inúteis porque isso já foi verificado em outro lugar. Mas e se alguém alterar esse código em outro lugar e remover ou alterar as verificações para não corresponder mais às suas condições prévias?

  3. Crescimento orgânico. Em grandes projetos, ao longo dos anos, muitas coisas mudam, e acontece que alguns métodos usados ​​antes não são mais usados, mas ninguém se preocupou em removê-los, pois ninguém controlava se esse método específico era usado ou não, eles apenas refatoravam seus pedaços de código e, por acaso, todos pararam de usar esse método. Ou condições que antes tinham significado, mas a aplicação foi refatorada em outros lugares, para que a condição se tornasse sempre verdadeira, mas ninguém se preocupou em removê-la.

  4. Projeto excessivo. As pessoas podem codificar algumas coisas "apenas no caso de precisarmos" e nunca realmente precisam. Como "vamos criar um tópico, caso tenhamos que fazer algum trabalho offline" e, em seguida, ninguém pede para fazer algo offline, e o programador esquece e passa para outros projetos (ou talvez para outra empresa), e esse código permanece lá para sempre, porque ninguém sabe por que está lá ou se é seguro removê-lo.

Portanto, embora eu nunca tenha visto isso por malícia ou por uma abordagem equivocada da segurança no trabalho, já vi várias vezes isso acontecer como resultado natural do desenvolvimento de software.


22
Penso que # 3, o Crescimento Orgânico explica uma grande fração do Código Inútil que vi no trabalho. Mas todas essas quatro razões assumem um programador inteligente. Algum código inútil deriva de alguém que não entende o que precisa acontecer e o que não acontece, e deixa muito código puramente por medo de mudar o que (meio que) funciona.
precisa

2
Vi o item 4 no meu projeto: geralmente não é feito de propósito para ter mais poder dentro da empresa, mas há pessoas que sempre tentam criar uma solução mais geral, mesmo que não seja necessária. Com relação ao item 2, eu mesmo o uso muito exatamente pelas razões que você explicou: IMHO, mesmo a menor função ou método não deve fazer nenhuma suposição sobre como o resto do código funciona ou será alterado. Em vez disso, meu código segue o padrão simples: "se a entrada está OK, então gera outro erro". Isso segue o princípio geral de design de minimizar dependências.
Giorgio

3
Você também esqueceu: desenvolvedores ruins. Algumas pessoas que estão escrevendo código não deveriam, e bons processos de revisão não existem em muitas lojas.
Joe

20

Minha pergunta é. Alguém mais viu código assim? Qual foi sua conclusão sobre por que esse código estava lá?

1) sim

2) Nos casos que eu vi, eu colocaria isso de várias maneiras:

  • Inexperiência do programador
  • Programador que não entende um design particularmente complicado e / ou mal executado que está tentando modificar
  • Programador sendo interrompido no meio de (digamos) um refator.
  • Descuido

Agora talvez eu esteja sendo caridoso com isso, mas minha abordagem geral é que é melhor perdoar / não confrontar essas coisas, do que apontar os dedos e continuar com a má qualidade. Obviamente, as coisas podem ficar ruins o suficiente para que algo tenha que ser feito , mas um leve empurrão na direção certa geralmente é suficiente.

Obviamente, você não pode adotar uma abordagem de laissez-faire se a qualidade / erros impactarem seriamente "os negócios". Mas nessa situação, você precisa de revisões de código obrigatórias e diligentes de tudo, combinadas com um procedimento de teste abrangente.


Na minha experiência, as pessoas tendem a ficar "rígidas" com o código de baixa qualidade (pelo menos em parte), porque isso ofende seus padrões pessoais. É muito bom (pessoalmente) buscar a perfeição, mas não é razoável projetar seus padrões pessoais em outras pessoas. Pelos sons das coisas (por exemplo, pela natureza de seus exemplos), é isso que você está fazendo.

Na IMO, isso não é produtivo e não conduz a um bom relacionamento de trabalho com seus colegas de trabalho.


+1 Estava digitando uma resposta e encontrou você praticamente listado todos os motivos que eu iria mencionar.
amigos estão

+1 por ser caridoso. Corrija os erros de alguém sem apontar os dedos e seus colegas respeitarão suas habilidades técnicas e de pessoal. Diga ao autor o código de baixa qualidade e seus colegas se ressentirão de sua abordagem e diminuirão suas habilidades.
Caleb

13

Todos esses são sintomas de como o projeto envelhece.

 1. Chamadas de método ou função que não fazem nada de valor. Muitas vezes, quando algum código é deixado como está (espero que com um grande aviso preterido , mas como a maioria dos idiomas não tem esse aviso, ele nem sempre é seguido ...) porque, a certa altura, ele serviu a alguns propósito genuíno e ninguém sabia o que poderia acontecer se as linhas ofensivas fossem removidas.

Eu me lembro disso de um dailywtf:

@deprecated // he might have been crazy enough to use reflection...
boolean getTrue() {
    return false; 
}

 2. Verificações redundantes feitas em um arquivo de classe, objeto ou método separado. As camadas de comunicação também são imperfeitas (leia o Mês do Homem Mítico? Se não, o que você está fazendo no computador !? Vá! LEIA!). Frequentemente, uma pessoa trabalha em algo e sai do projeto, e o próximo, encontrando algum bug bizarro, lança uma verificação extra aqui e ali para tentar eliminá-lo. Quando o bug é removido, as verificações não são porque, bem, se não estiver quebrado, não o conserte.

 3. Se declarações que sempre avaliam como verdadeiras. Oh, eu fiz este. Eu recebi um projeto uma vez, ele tinha uma série de provavelmente 10 a 15 blocos if / else . Para mudar o comportamento, basta colocar um true||no primeiro bloco. Não foi até meses (anos?) Depois que voltei e disse: "Uau, esse código deveria ter sido destruído, mas nunca foi"

 4. Tópicos que se desdobram e não fazem nada de especial. Eu posso imaginar uma linha de pensamento assim:

  1. Eu sei! Eu posso lidar com esses dois problemas de forma assíncrona! Vou criar tópicos foo e bar.
  2. (dois meses depois) Ah, você sabe, a funcionalidade do bar é um pouco melhor em foo. Vou me mudar um pouco.
  3. (um ano depois) Você sabe, colocar essas outras coisas de bar em foo faz sentido.
  4. (muitos, muitos anos depois) "Ei, esse bartópico não parece estar fazendo nada, podemos removê-lo?" "Melhor não, já existe há muitos, muitos anos ..."

5
+1 para "Melhor não, já existe há muitos e muitos anos ..." - isso acontece várias vezes. Medo de remover por causa do medo das consequências ("Como testamos que não quebramos algo" - especialmente se não há testes de unidade por aí).
quickly_now

11

Sou um pouco mais otimista. Eu acho que o que você descreveu geralmente ocorre quando o código é refatorado descuidadamente.


13
Embora seja difícil, nunca atribua à Malícia o que pode ser explicado pela estupidez.
precisa

8

Velhos companheiros me contaram uma época em que os consultores eram pagos pelo número de linhas de código que produziam. E, assim, maximizavam os lucros usando construções surpreendentemente longas.

Hoje em dia, eu sempre assumo que o cara ainda está aprendendo o idioma enquanto faz o trabalho. E ele está com pressa.


Fale sobre cortar o nariz para irritar seu rosto. Eu acho que tudo bem se você nunca mais precisar olhar o código novamente.
JeffO 22/08/11

6

A maioria das respostas se resume a esses dois fatos simples:
[1] Código reflete o histórico do código e
[2] Código reflete o futuro esperado do código.

Escrevi funções que não têm valor algum, NO AMBIENTE ATUAL DE APLICAÇÃO, dadas as ESPECIFICAÇÕES ATUAIS, mas podem ser necessárias no futuro.

Escrevi declarações if que, NO PRESENTE, sempre são avaliadas como verdadeiras. Mas talvez no passado pudesse ser falso.

Quanto aos cheques redundantes, ei, eu não confio em outro código, nem confio em meu próprio código. Se um módulo depende de N ser 1 ou 2 ou 3, é muito melhor garantir isso, e falhar informativamente, se não for. É ilegítimo o Módulo B explodir porque o Módulo A estragou tudo; é bastante legítimo que o Módulo B reclame que o Módulo A estragou tudo. E lembre-se de que, no próximo mês, esse parâmetro pode estar vindo do módulo C. ainda não escrito


1
Eu chamo isso de má codificação. Você espera que seja necessário no futuro, mas isso raramente acontece. YAGNI. Escrever um if que sempre avalia como verdadeiro é um esforço desperdiçado e confunde a pessoa que deve adicionar uma funcionalidade bastante provável para começar. Esse parâmetro que vem no próximo mês pode esperar até o próximo mês para ser adicionado. O código desorganizado AGORA é inútil.
Andy

1
if (language = 'en' ou language = th ') - talvez no próximo mês tentemos chinês? if (! isset ($ TITLE)) - todos os módulos devem definir $ TITLE, mas talvez alguém algum dia escreva errado. if (file_exists ($ TARGET)) - um bom código já terá criado o arquivo, mas talvez haja um erro no sistema e ele não foi criado. Meu código de interface PHP / MySQL padrão sempre verifica erros, mesmo que eu nunca tenha pegado um ainda.
Andy Canfield

3

Eu já vi isso algumas vezes, na verdade ontem, eu tenho que mesclar alguns dos códigos dos meus chefes no meu novo aplicativo. No caso dele, tudo se resume a uma falta geral de habilidade e entendimento e a crença de que ele pensa que é um desenvolvedor bastante habilidoso.

'Chamadas de método ou função que não fazem nada de valor.' e 'se declarações que sempre avaliam como verdadeiras' são um problema importante com seu código.


3

Suspeito que, embora muitos tenham visto um código com esses problemas, poucos se importariam em escrever o mesmo. Com toda a probabilidade, o que você está vendo é uma podridão acumulada de software - alguém adiciona algo, o que realmente não funciona, o próximo mantenedor adiciona código de proteção mais adiante na cadeia para se proteger contra a condição que não foi verificada corretamente no primeiro Lugar, colocar; alguém recebe um relatório de problemas e adiciona ainda mais blindagem a uma instância específica de um problema; outra pessoa adiciona uma verificação mais geral e esquece de remover parte do código antigo adicionado anteriormente, que tratava de sintomas mais específicos etc.

Depois, há o problema da limpeza do código: não há incentivo específico para remover o que parece ser um código morto e um tremendo incentivo para não fazê-lo, porque se você não entender o código completamente, sua avaliação de que o código está "morto" será falho e você acabará quebrando o sistema.


2
  • Chamadas de método ou função que não fazem nada de valor.

Não necessariamente ruim. Os métodos em uma classe base geralmente chamam métodos vazios que são considerados pontos de substituição para subclasses. Exemplo: o UIView do Cocoa Touch possui um -didAddSubview:método documentado como não fazendo nada na versão padrão. O -addSubview:método do UIView precisa chamar, -didAddSubview:mesmo que não faça nada, porque as subclasses podem implementá-lo para fazer alguma coisa. Métodos que não fazem nada e as razões para eles devem ser documentados, é claro.

Se uma função / método vazio ou inútil estiver obviamente presente por razões históricas, deve ser excluído. Dê uma olhada nas versões anteriores do código no seu repositório de códigos-fonte, se você não tiver certeza.

  • Verificações redundantes feitas em um arquivo de classe, objeto ou método separado.

Difícil dizer se está tudo bem sem algum contexto. Se as verificações forem claramente feitas pelo mesmo motivo, isso pode significar que não há uma separação clara de responsabilidades e é necessária alguma refatoração, especialmente quando as duas verificações resultam na mesma ação. Se a ação resultante das duas verificações não for a mesma, as duas verificações provavelmente estão sendo realizadas por motivos diferentes, mesmo que a condição seja a mesma, e isso provavelmente está correto.

  • declarações if sempre avaliadas como verdadeiras.

Há uma grande diferença entre:

if (1) {
    // ...
}

e:

if (foo() == true) {
    // ...
}

onde foo()acontece sempre retornar true.

O primeiro caso acontece muito quando as pessoas estão depurando. É fácil usar um if (0) {...para remover temporariamente um pedaço de código enquanto você tenta isolar um bug e depois alterar 0para 1para restaurar esse código. Ele ifdeve ser removido assim que você terminar, é claro, mas é fácil esquecer essa etapa ou perder uma ou duas, se você a tiver feito em vários lugares. (É uma boa idéia identificar esses condicionais com um comentário que você possa pesquisar mais tarde.) O único dano é a confusão que isso pode causar no futuro; se o compilador puder determinar o valor da condição em tempo de compilação, ele a removerá completamente.

O segundo caso pode ser aceitável. Se a condição representada por foo()precisar ser testada em vários locais do código, fatorá-la em uma função ou método separado é geralmente a coisa certa a ser feita, mesmo que foo()sempre seja verdadeira agora. Se for concebível que foo()possa eventualmente retornar false, isolar essa condição em um método ou função é uma maneira de identificar todos os locais onde o código se baseia nessa condição. No entanto , isso cria algum risco de que a foo() == falsecondição não seja testada e possa levar a problemas mais tarde; a solução é garantir que você adicione testes de unidade que testam explicitamente o falsecaso.

  • Tópicos que se desdobram e não fazem nada de importante.

Isso soa como um artefato da história e algo que pode ser identificado durante uma revisão de código ou por meio de criação de perfil periódica do software. Suponho que poderia ser criado intencionalmente, mas tenho dificuldade em imaginar que alguém realmente faria isso de propósito.


1

Acontece. Muitas vezes, na verdade.

Às vezes, esses becos sem saída de codificação são mais como trilhas antigas de cabras que caíram em ruínas quando uma rodovia mais eficiente / moderna / rápida foi instalada em torno deles.

Em outras ocasiões (e provavelmente sou culpado disso), elas são as bases para a extensão do software quando um conjunto de recursos / funções informado, mas não confirmado, é solicitado. (Às vezes, colocar um pouco de trabalho na compilação inicial, fornecendo alças e similares para trabalhos posteriores que você planeja usar "pode ​​tornar a vida mais fácil, quando isso acontece, ou mais difícil / confusa, se o trabalho posterior não acontecer" evento.)

E muito disso se deve diretamente ao antigo "Se não está quebrado, não encaixe". Às vezes, arrancar o código que você não entende ou que acredita não ser usado pode causar a versão de programação de "The Butterfly Effect". Isso já aconteceu uma ou duas vezes também.


1

Às vezes, tenho um booleano global definido como true e, mais tarde, no meu código, um if (bool), durante o tempo de execução, posso definir um ponto de interrupção na instrução if e alternar o booleano para testar alguma coisa.


0

Oponho-me a que as if truedeclarações sejam classificadas indiscriminadamente como "código sem sentido".

Existe um argumento legítimo para o uso de um if (1) { ... }bloco no código C que deseja ser compatível com o padrão C que insistia em que as definições de variáveis ​​estivessem no início de uma função ou apenas queria que as variáveis ​​locais fossem definidas o mais local possível.

switch (i) {
    case 23:
        if (1) {
            /* I can declare a local var here! */
        }
        break;
}

5
Não há necessidade do 'if (1)', por que não apenas ter o bloco?
FigBug 22/08/19

3
Tanto o C / C ++ quanto o C #, e tenho certeza que o Java (assim como muitas outras linguagens similares) permitem blocos de instruções anônimos; Não há necessidade de um if, whileou construção semelhante. É improvável que seja muito limpo, mas certamente é permitido de acordo com as especificações do idioma.
um CVn

0

Um professor meu contou-nos um dia uma história de que um empregador anterior os pagaria com base no número de linhas que eles completaram. Então, eles escreveram várias funções alinhadas com várias dúzias que nunca foram chamadas. Parece um ótimo motivo para escrever código inútil.


0

Como o @Andy mencionou, um grande componente que eu vi está quebrando o YAGNI .

Alguém começa com uma suposição sobre o que todos os elementos serão em vez do que muitos elementos podem precisar , o que é uma confusão dos relacionamentos "é um" e "tem um" .

Essa confusão leva a uma estrutura rígida de herança e, em seguida, são métodos que não são implementados porque na verdade nunca são chamados, lógica repetida em que partes precisam ser aprimoradas e fluxos de trabalho geralmente estranhos que não se alinham ao modelo de negócios.

Como muitos outros aqui, eu não vi isso feito com intuito malicioso, mas por falta de conhecimento de um bom design e pela tentativa de fazer com que pareça assim para os outros. Engraçado, parece que os desenvolvedores ainda menos informados parecem se sair melhor nesse sentido, porque pelo menos o código deles não acaba sendo excessivamente projetado ad naseum. ( Princípio KISS )

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.