Como evitar sempre a sensação de que, se eu reconstruísse completamente meu programa do zero, faria muito melhor? [fechadas]

91

Eu aprendi uma quantidade significativa de codificação, no entanto, sempre esteve em um ambiente científico (não em ciência da computação), completamente autodidata, sem ninguém para me guiar na direção certa. Assim, minha jornada de codificação foi ... bagunçada. Percebi agora que, sempre que construo algum tipo de programa, no final, percebo como poderia ter feito isso de maneira muito mais elegante, muito mais eficiente e de uma maneira muito mais flexível e fácil de gerenciar no futuro. Em algumas circunstâncias, eu realmente voltei e reconstruí as coisas desde o início, mas geralmente isso não é praticamente possível. Embora a maioria dos meus programas até agora tenha sido relativamente pequena, parece bastante complicado reescrever completamente programas grandes toda vez que você cria algo.

Só estou pensando, isso é uma experiência normal? Caso contrário, como você evita que isso aconteça? Eu tentei planejar as coisas com antecedência, mas não consigo realmente prever tudo até começar a elaborar algum código.

Ashish
fonte
87
seu normal, programa mais
Ewan
7
Relevante .
Neil
43
Bem-vindo à programação. Se você não olhar para o código antigo e pensar "urgh", comece a se preocupar, pois significa que parou de aprender.
Caltor 26/10/19
9
Honestamente, quando vi essa pergunta, ri - porque literalmente todo programador que já falei também, inclusive eu, tem esse sentimento, constantemente, o tempo todo. Se você quisesse entrar em um campo em que sentia que o produto final que criava não apresentava falhas - a programação era o campo errado para escolher.
Zibbobz 26/10/19

Respostas:

4

Esse sentimento é completamente normal e esperado. Isso significa que você está aprendendo. Quase toda vez que inicio um novo projeto, começo em uma direção e acabo desenhando de maneira diferente no final.

É prática comum primeiro desenvolver um protótipo antes de iniciar a coisa real. Também é comum revisitar o código antigo e refatorá- lo. Isso é mais fácil se o seu design for modular - você pode redesenhar facilmente partes e peças de cada vez, sem precisar descartar totalmente o design antigo.

Para algumas pessoas, é melhor escrever primeiro o pseudo-código . Outros acham útil começar escrevendo comentários descrevendo o que o código fará, depois escreva o código assim que a estrutura geral estiver lá. Essas duas coisas ajudarão você a planejar melhor seu design e podem evitar a necessidade de uma reescrita.

Se você faz parte de uma equipe, ter um processo de revisão é crucial para o processo de design. Peça a alguém que revise seu código para aprender como melhorar seu design.

asrjarratt
fonte
100

Esta é uma experiência muito comum

A maioria das pessoas com quem interajo e eu também me sinto assim. Pelo que posso dizer, uma razão para isso é que você aprende mais sobre o domínio e as ferramentas usadas ao escrever seu código, o que o leva a reconhecer muitas oportunidades de melhoria depois de escrever seu programa.

O outro motivo é que você pode ter uma idéia na cabeça sobre a solução ideal de código limpo e, em seguida, o mundo real e suas limitações complicadas atrapalham, forçando você a escrever soluções e hacks imperfeitos que podem deixá-lo insatisfeito.

"Todo mundo tem um plano até levar um soco na cara."

o que fazer sobre isso

Até certo ponto, você terá que aprender a aceitar que seu código nunca será perfeito. Uma coisa que me ajudou nisso é a mentalidade de "Se eu odeio o código que escrevi há um mês, significa que aprendi e me tornei um programador melhor".

Uma maneira de aliviar o problema é estar sempre atento a possíveis melhorias enquanto trabalha e refatora continuamente. Certifique-se de atingir um bom equilíbrio entre refatorar e adicionar novos recursos / corrigir bugs. Isso não ajudará em grandes problemas de design, mas geralmente o deixará com uma base de código mais polida da qual você pode se orgulhar.

Andreas Kammerloher
fonte
18
A única coisa que eu acrescentaria a essa resposta é aprender e seguir os princípios do TDD ao escrever o código. Esses testes fornecem um ambiente seguro para refatoração e reescrita, à medida que você continua aprimorando como desenvolvedor e deseja fazer alterações no código existente.
David Arno
10
@DavidArno Qual é o sentido de refatorar com os testes presentes? É como atualizar um sistema e fazer um backup antes de ... 0 diversão.
26518 Džuris
3
@ Džuris, :) É verdade que, se você gosta de desafios, não escreva testes
David Arno
6
@ Džuris É como pular de um avião de pára-quedas!
JollyJoker # 26/18
3
@jamesqf - É uma descaracterização do que o TDD e os testes de unidade fazem - por exemplo, eles podem dizer que seu modelo ou algoritmo foi implementado corretamente e que você não quebrou nada ao refatorar. Eles não podem dizer que seu modelo é realmente útil. Isso é verdade tanto em um ambiente científico quanto em qualquer outro.
Ant
46

Aprenda refatoração - a arte de melhorar gradualmente o código. Todos aprendemos o tempo todo, por isso é muito comum perceber que o código que você mesmo escreveu pode ser melhor.

Mas você deve poder transformar o código existente para aplicar essas melhorias sem precisar começar do zero.

JacquesB
fonte
4
Essa prática também o ajudará a aprender a escrever código modular e que pode ser refatorado com mais facilidade. Todas as palestras do mundo não me ensinaram esse hábito, mas precisaram dividir velhas funções grandes em pedaços administráveis, para que eu não tivesse que reescrever do zero. A cada novo projeto, faço melhor.
JKreft # 26/18
O melhor ponto de partida, na minha opinião para aprender sobre refatoração é: Refactoring é sobre os novos recursos
Wildcard
9

Se você tem excelentes requisitos estáticos e os entende bem e tem tempo para análises detalhadas, você tem a chance de criar um bom design com o qual ainda ficará satisfeito quando terminar.

Mesmo nesse caso feliz, você pode aprender novos recursos de linguagem que teriam ajudado a criar um design melhor.

No entanto, geralmente, você não terá essa sorte: os requisitos serão menos que estelares e incompletos e, embora você tenha pensado que os entendeu, acontece que existem áreas sobre as quais você fez suposições inválidas.

Em seguida, os requisitos serão alterados à medida que os usuários analisarem suas entregas iniciais. Então, algo que os usuários não controlam mudará, por exemplo, a lei tributária.

Tudo o que você pode fazer é projetar, assumindo que as coisas vão mudar. Refatore quando puder, mas saiba que tempo e orçamento geralmente significam que sua entrega final não é tão elegante quanto teria sido se você soubesse no início o que sabe agora.

Com o tempo, você vai se aprofundando nos requisitos que recebe e obtém um nariz para o qual partes de seu projeto provavelmente precisam de flexibilidade para absorver as mudanças.

No final, aceite que a mudança é constante e ignore a pontada que diz "Eu poderia ter feito melhor". Seja orgulhoso e feliz por ter fornecido uma solução.

Wildbill
fonte
Ou, de uma maneira diferente: Aprenda a projetar para obter flexibilidade desde o início, sem introduzir custos indiretos desnecessários. Idealmente, a flexibilidade deriva da simplicidade. Então seu projeto tem a chance de passar no teste de requisitos em mudança.
C26 de
3

Como evitar sempre a sensação de que, se eu reconstruísse completamente meu programa do zero, faria muito melhor?

O que você pode fazer é criar um protótipo descartável, antes de começar a fazer o projeto "real". Rapido e sujo. Então, quando você obtém um protótipo para provar um conceito, conhece o sistema e como fazer as coisas corretamente.

Mas não se surpreenda, se depois de N anos você voltar a esse código e pensar "que bagunça".

BЈовић
fonte
3
Ou você mostra o protótipo ao seu chefe, ele diz: 'Brilhante, isso serve'. move você para outro projeto e seu protótipo se torna produção.
RyanfaeScotland
2
Esse conselho é tão comum que há um ditado sobre isso. "Construa um para jogar fora". E esse conselho é tão mal usado que existe um ditado a respeito: "Se você construir um para jogar fora, provavelmente jogará fora dois".
Eric Lippert
2
@EricLippert Minha experiência foi terrível na direção oposta - se criarmos uma para jogar fora, ela inevitavelmente será enviada ao cliente.
26418 Jon
2
@ Jon: Sim, isso pode acontecer, principalmente quando erros de gerenciamento "a interface do usuário parece que funciona", na verdade com qualquer código lá. Dito isto, minha experiência sempre foi, como um dos meus colegas de trabalho costumava dizer: "temos tempo suficiente para errar duas vezes, mas não há tempo suficiente para acertar uma vez". :-)
Eric Lippert 26/10
@EricLippert Essa é exatamente a razão para criar a GUI por último, e NÃO criar a GUI para um protótipo;) #
30718
3

Lembre-se deste mantra:

O perfeito é o inimigo do bem .

A solução perfeita nem sempre é a solução ideal . A solução ideal é aquela que atinge o status "bom o suficiente" com a menor quantidade de trabalho.

  • Ele atende a todos os requisitos de funcionalidade e desempenho?
  • Está livre de erros críticos que você não pode corrigir na arquitetura atual?
  • Estime quanto trabalho você investirá para manter esse aplicativo no futuro. O esforço de reescrever seria maior do que o esforço de longo prazo que economizaria?
  • Existe a possibilidade de seu novo design realmente piorar as coisas?

Se você responder sim a todas essas perguntas, seu software é "bom o suficiente" e não há boas razões para reescrevê-lo do zero. Aplique as lições de design que aprendeu no seu próximo projeto.

É perfeitamente normal que todo programador tenha alguns programas complicados no passado. Aconteceu várias vezes ao longo da minha carreira como desenvolvedor de software que eu olhei para algum código, me perguntei "Que idiota escreveu essa bagunça?", Verifiquei o histórico da versão e percebi que era eu de vários anos atrás.

Philipp
fonte
3

Eu tentei planejar as coisas com antecedência, mas não consigo realmente prever tudo até começar a elaborar algum código.

É tentador pensar que o planejamento perfeito fornecerá a arquitetura / arquitetura de software perfeitas, no entanto, isso é categoricamente falso. Há dois grandes problemas com isso. Primeiro, "no papel" e "o código" raramente correspondem, e o motivo é que é fácil dizer como deve ser feito, em vez de realmente fazê-lo . Em segundo lugar, mudanças imprevistas nos requisitos tornam-se aparentes no final do processo de desenvolvimento que não poderiam ter sido pensadas desde o início.

Você já ouviu falar do movimento Agile? É uma maneira de pensar em que valorizamos "reagir à mudança" em vez de "seguir um plano" (entre outras coisas). Aqui está o manifesto (é uma leitura rápida). Você também pode ler sobre o Big Design Up Front (BDUF) e quais são as armadilhas.

Infelizmente, a versão corporativa do "Agile" é um monte de falsos (mestres de scrum certificados, processo pesado em nome de "Agile", forçando o scrum, forçando 100% de cobertura de código etc.) e geralmente resulta em mudanças de processo asininas porque os gerentes pense que o Agile é um processo e uma bala de prata (do qual não é nem um). Leia o manifesto ágil, ouça pessoas que iniciaram esse movimento, como tio Bob e Martin Fowler, e não se deixe levar pela versão sem sentido de "corporate Agile".

Em particular, você geralmente pode se dar bem apenas com o TDD (Test Driven Development) em código científico , e há uma boa chance de que seu projeto de software seja muito bom. Isso ocorre porque o código científico de sucesso tem principalmente interfaces ultra-utilizáveis, com desempenho como uma preocupação secundária (e às vezes concorrente), e assim você pode se dar bem com um design mais "ganancioso". O tipo TDD força seu software a ser ultrautilizável , porque você escreve como deseja que as coisas sejam chamadas (idealmente) antes de implementá-las. Também força pequenas funções com pequenas interfaces que podem ser chamadas rapidamente de uma maneira simples, "entrada" / "saída", e coloca você em uma boa posição para refatorar caso os requisitos mudem.

Acho que todos podemos concordar que numpyé um software de computação científica bem-sucedido. Suas interfaces são pequenas, super utilizáveis ​​e tudo funciona bem juntas. Observe que numpyo guia de referência recomenda explicitamente o TDD: https://docs.scipy.org/doc/numpy-1.15.1/reference/testing.html . Eu usei o TDD no passado para o software de imagem SAR (Synthetic Aperature Radar): e também posso afirmar que funciona extremamente bem para esse domínio em particular.

Advertência: A parte de design do TDD funciona menos bem em sistemas onde uma refatoração fundamental (como decidir que você precisa que seu software seja altamente simultâneo) seria difícil, como em um sistema distribuído. Por exemplo, se você tivesse que projetar algo como o Facebook, onde você tem milhões de usuários simultâneos, fazer TDD (para orientar seu design) seria um erro (ainda aceitável para usar depois de um design preliminar e apenas "testar o primeiro desenvolvimento" "). É importante pensar nos recursos e na estrutura do seu aplicativo antes de entrar no código. O TDD nunca o levará a um sistema distribuído e altamente disponível.

Como evitar sempre a sensação de que, se eu reconstruísse completamente meu programa do zero, faria muito melhor?

Dado o exposto, deve ser um pouco evidente que um design perfeito é realmente impossível de alcançar; portanto, perseguir um design perfeito é um jogo de tolos. Você realmente pode apenas chegar perto. Mesmo se você acha que pode redesenhar do zero, provavelmente ainda existem requisitos ocultos que não se mostraram. Além disso, as reescritas levam pelo menos o tempo necessário para desenvolver o código original. Quase certamente não será mais curto, pois é provável que o novo design tenha problemas próprios imprevistos, além de você ter que reimplementar todos os recursos do sistema antigo.

Outra coisa a considerar é que seu design realmente importa quando os requisitos mudam .Não importa o quão ruim é o design, se nada mudar (supondo que seja totalmente funcional para os casos de uso atuais). Eu trabalhei em uma linha de base que possuía uma instrução de troca de 22.000 linhas (a função era ainda mais longa). Foi um design terrível? Caramba, sim, foi horrível. Nós consertamos isso? Não. Funcionou muito bem do jeito que estava e essa parte do sistema nunca realmente causou falhas ou bugs. Ele só foi tocado uma vez nos dois anos em que estive no projeto e alguém, você adivinhou, inseriu outro caso no comutador. Mas não vale a pena arrumar tempo para consertar algo que é tocado com pouca frequência, simplesmente não é. Deixe o design imperfeito como ele é e, se não estiver quebrado (ou constantemente quebrado), não o conserte. Então talvez você possa fazer melhor ... mas valeria a pena reescrever? O que você vai ganhar?

HTH.

Matt Messersmith
fonte
2

Concordo plenamente com a resposta fornecida por Andreas Kammerloher, mas estou surpreso que ninguém tenha sugerido aprender e aplicar algumas práticas recomendadas de codificação. É claro que isso não é uma bala de prata, mas usar uma abordagem de orientação aberta, padrões de design, entender quando seu código cheira e assim por diante fará de você um programador melhor. Investigue qual é o melhor uso de bibliotecas, estruturas etc. Há muito mais com certeza, estou apenas arranhando a superfície.

Isso não significa que você não verá seu código antigo como um lixo total (na verdade, você verá os programas mais antigos ainda mais lixo do que sem esse conhecimento), mas com cada novo software que você escrever verá você melhora. Observe também que o número de práticas recomendadas de codificação aumenta com o tempo; algumas simplesmente mudam para que você nunca chegue à perfeição. Aceite este ou bastante o caminho.

Mais uma coisa boa é ter uma revisão de código. Quando você trabalha sozinho, é fácil cortar custos. Se você tiver uma segunda pessoa revisando seu código, eles poderão indicar onde você não segue essas práticas recomendadas. Dessa forma, você produzirá um código melhor e aprenderá algo.

Ister
fonte
1

Para adicionar outras excelentes respostas aqui, uma coisa que acho útil é saber para onde você quer chegar .

É raro receber a aprovação de uma grande parte da refatoração por conta própria. Mas muitas vezes você pode fazer pequenos refatoramentos à medida que avança, 'sob o radar', enquanto trabalha em cada área da base de código. E se você tem um objetivo em mente, pode aproveitar essas oportunidades para avançar, passo a passo, na direção certa.

Pode levar muito tempo, mas a maioria das etapas melhorará o código e o resultado final valerá a pena.

Além disso, sentir que você poderia fazer melhor é um bom sinal ! Isso mostra que você se preocupa com a qualidade do seu trabalho e está avaliando-o criticamente; então você provavelmente está aprendendo e melhorando. Não deixe que essas coisas o preocupem - mas não pare de fazê-las!

gidds
fonte
1

Você inadvertidamente tropeçou em um dos maiores desafios da humanidade (aventuras), a ponte entre homem e máquina. A ponte entre o homem e a estrutura física, engenharia civil, por exemplo, está em andamento há cerca de 200 anos ou mais.

Como o desenvolvimento de software realmente só se tornou popular nos anos 90, ele tem aproximadamente 30 anos. Aprendemos que não é tanto uma disciplina de engenharia quanto uma ciência social, e apenas começamos.

Sim, você experimentará TDD, Refatoração, Programação Funcional, Padrão de Repositório, Fonte de Eventos, MV alguma coisa, Script Java (<- Faça isso, é loucura), Vinculação de Modelo, Sem Sql, Containers, Agile, SQL (<- faça isso é poderoso).

Não há uma solução. Até os especialistas ainda estão entendendo os canudos.

Bem-vindo, e esteja avisado, é um lugar solitário; mas absolutamente fascinante.

Marius
fonte
1

Eu vou ir um pouco contra a corrente. Isso é incrivelmente comum , mas não é aceitável . O que isso indica é que você não está reconhecendo boas maneiras de organizar seu código enquanto o escreve. A sensação vem do seu código não ser direto .

Sua experiência foi minha por um longo tempo também, mas recentemente (nos últimos dois anos), venho produzindo mais códigos que não me fazem sentir que preciso jogar tudo fora. Aqui está aproximadamente o que eu fiz:

  1. Seja claro sobre as suposições que qualquer bloco de código específico está fazendo. Lance erros se eles não forem cumpridos. Pense nisso isoladamente , sem depender de detalhes sobre o que o restante do software está fazendo. (O que o restante do software está fazendo influencia as suposições que você aplica e quais casos de uso são suportados, mas não influencia se você gera um erro quando uma suposição é violada.)
  2. Pare de acreditar que seguir regras, padrões e práticas produzirá um bom código. Jogue fora mentalidades e práticas que não são óbvias e diretas. Este era enorme. OO e TDD são geralmente ensinados de uma maneira que não se baseia em considerações práticas, como um conjunto de princípios abstratos que você deve seguir ao escrever código. Mas isso é completamente inútil para realmente desenvolver um bom código. Se você usar OO ou TDD, ele deve ser usado como solução para os problemas que você entende que possui . Em outras palavras, eles devem ser usados ​​apenas quando você olha para um problema e pensa: "Ok, isso faz todo sentido e é extremamente óbvio como uma boa solução". Não antes.
  3. Ao escrever código, concentre-se em duas perguntas:
    • Estou usando recursos e bibliotecas da maneira como foram projetados para serem usados ? Isso inclui usá-lo para os tipos de problemas que se destinava a resolver, ou pelo menos muito semelhantes.
    • É simples ? Meus colegas de trabalho e eu seremos capazes de seguir a lógica facilmente mais tarde? Existem coisas que não são imediatamente óbvias no código?

Meu código agora é mais "processual", com o que quero dizer que está organizado por quais ações são executadas e não por quais estruturas de dados ele usa. Eu uso objetos em linguagens em que funções independentes não podem ser substituídas em tempo real (C # e Java não podem substituir funções em andamento, o Python pode). Eu tenho a tendência de criar mais funções utilitárias agora, que apenas afastam alguns clichês irritantes do caminho para que eu possa realmente ler a lógica do meu código. (Por exemplo, quando eu precisava processar todas as combinações de itens em uma lista, empurrei o índice em loop para um método de extensão que retornaTuples, para que a função original não seja confusa com esses detalhes de implementação.) Agora passo muito mais coisas como parâmetros para funções, em vez de ter uma função alcançando algum outro objeto para buscá-la. (O chamador busca ou o cria e o passa e o passa.) Agora deixo mais comentários que explicam coisas que não são óbvias apenas olhando o código , o que facilita o acompanhamento da lógica de um método. Só escrevo testes em casos limitados em que me preocupo com a lógica de algo que acabei de criar e evito usar zombarias. (Faço mais testes de entrada / saída em partes isoladas da lógica.) O resultado é um código que não é perfeito , mas que realmente parece bom, mesmo 2 ou 3 anos depois. É um código que responde razoavelmente bem à mudança; pequenas coisas podem ser adicionadas ou removidas ou alteradas sem que todo o sistema desmorone.

Até certo ponto, você precisa passar por um período em que as coisas estão uma bagunça, para que você possa ter alguma experiência. Mas se as coisas ainda estão tão confusas que você quer jogar tudo fora e começar de novo, algo está errado; você não está aprendendo.

jpmc26
fonte
0

Lembrando que seu tempo é limitado. E seu tempo futuro também é limitado. Seja para trabalho, escola ou projetos pessoais, quando se trata de código de trabalho , você deve se perguntar "está reescrevendo esse o melhor uso do meu tempo limitado e valioso?". Ou talvez "esse é o uso mais responsável do meu tempo limitado"?

Às vezes a resposta será inequivocamente positiva . Geralmente não. Às vezes, estará em cima do muro e você terá que usar sua discrição. Às vezes, é um bom uso do seu tempo simplesmente por causa das coisas que você aprenderá fazendo isso.

Eu tenho muitos projetos, profissionais e pessoais, que se beneficiariam de uma porta / reescrita. Eu também tenho outras coisas para fazer.

Jared Smith
fonte
0

É uma parte completamente normal da aprendizagem. Você percebe erros à medida que avança.

É assim que você melhora e não é algo que você deva evitar.

mathreadler
fonte
0

Você pode se dar a experiência de saber que o desejo sedutor de reescrever é geralmente improdutivo. Encontre algum projeto de código aberto antigo, cabeludo e deselegante, de complexidade moderada. Tente reescrevê-lo do zero e veja como você o faz.

  • Seu design do zero acabou tão elegante quanto você esperava?
  • Sua solução é realmente exatamente o mesmo problema? Quais recursos você deixou de fora? Que casos extremos você perdeu?
  • Que lições suadas no projeto original você apagou na busca pela elegância?
  • Depois de adicionar as peças que faltam de volta ao seu design, é tão limpo quanto era sem elas?

Eventualmente, seu instinto mudará de pensar "eu poderia reescrever esse sistema muito melhor" para pensar "a fragilidade desse sistema pode ser indicativa de alguma complexidade que não é imediatamente aparente".

Maxpm
fonte