Acabei de ler o link do artigo que você postou, devo dizer que Fowler fez alguns pontos muito bons e muitas coisas que ele disse, venho advogando com nossa equipe há anos.
Na IMO, se você faz um design decente, não deve entrar no que seria considerado uma situação sem saída. Eu sempre vi o software como composto de blocos de construção . Ainda acredito em algum projeto inicial, mas o objetivo principal não é projetar o produto inteiro, mas fornecer uma arquitetura / direção geral para que sua equipe possa visualizar uma imagem comum na qual estamos trabalhando. Se você tem um monte de peças de cubo e triângulo, é útil esboçar como um castelo seria montado antes de você simplesmente começar a bater as peças.
Como eu venho da terra OO, para mim cada bloco é uma classe e a área de superfície desse bloco é a interface pública (o que é visível por classes externas ou derivadas). Se você seguir bons princípios do SOLID, garantirá que cada bloco seja extremamente simples e tenha uma interface pública intuitiva. Voltando à minha analogia, você quer ter certeza de que o código cria apenas formas simples. Sempre que você cria classes complexas demais (muitas funções, muitas variáveis), cria formas difíceis de reutilizar quando os requisitos mudam.
Concordo com Fowler em que o maior risco / desafio para o design evolutivo é que você deixa as decisões de design com tempo de codificação e espera que cada desenvolvedor individual tome essas decisões. É aqui que o sistema pode quebrar se você não tiver mecanismos de feedback adequados em vigor. Sempre que um novo recurso é solicitado, é extremamente tentador simplesmente encontrar a função que precisa ser estendida, colocar algum tipo de condicional dentro dele e apenas adicionar um monte de código dentro dessa função. E, às vezes, isso pode ser tudo o que é necessário, mas essa também é (IMO) a prática mais comum que leva a componentes sem saída. Isso não tem nada a ver com design evolucionário. Isso é chamado de "sem design".
Contanto que você reserve um tempo para recuar e dizer, espere um minuto, esta classe já possui 15 variáveis de membro, deixe-me extrair 6 delas e colocá-las em sua própria classe independente, seu software será composto de muita luz blocos de construção leves, flexíveis e reutilizáveis. Certamente, se os PMs aparecerem e mudarem metade dos requisitos de produtos, talvez seja necessário retirar alguns blocos, colocá-los novamente na prateleira e elaborar outros novos (como na construção de um castelo, você não pode usar todos seus cilindros). Mas, nesse ponto, isso faz parte dos negócios. Os requisitos foram alterados e, mantendo seu código flexível e modular, você poderá alterar seu produto para se alinhar com a nova direção de negócios.
Acredito que essa abordagem evolutiva do design funcione com todos os níveis de habilidade do engenheiro. Pessoalmente, faço software há muito tempo e, antes de nossa equipe mudar para a metodologia ágil, eu era responsável pelo envio de vários componentes principais do meu PC de desenvolvimento quase diretamente ao cliente, com quase nenhum controle de qualidade. Ao mesmo tempo, esses componentes sempre permaneceram flexíveis e sustentáveis.
Só estou tentando dizer que me consideraria relativamente decente ao projetar software. Ao mesmo tempo, se você me pedisse para escrever um documento de design de 100 páginas, entregá-lo a um codificador e esperar que funcionasse, provavelmente não conseguiria me projetar com um saco de papel. Ao iniciar o trabalho, algumas vezes eu esboçava alguns diagramas do tipo UML (muito simplificado, não em linguagem completa), mas, ao começar a codificar, refatava conforme necessário e meu código final nunca se pareceria com o que eu originalmente desenhei. Mesmo se eu passar um mês ou dois pensando em cada pequeno detalhe, não consigo imaginar alguém capaz de pegar meus diagramas e criar um software sólido sem modificar o design à medida que eles estão codificando.
No outro extremo do espectro, atualmente em minha equipe (agora ágil e eu apóio isso), temos alguns caras que se juntaram a nós de terras incorporadas, onde fizeram C apenas nos últimos 15 anos. Obviamente, eu ajudei no planejamento inicial e na criação de aulas, mas também me assegurei de acompanhar revisões regulares de código e sessões de brainstorming, nas quais discutimos aplicações do SOLID e dos princípios de design. Eles produziram algum código de espaguete que me fez estremecer um pouco, mas com apenas um leve empurrão de mim, eles começaram a refatorar o que já havia sido produzido e a parte engraçada é que um deles voltou para mim alguns dias depois e disse: Eu odeio Para dizer isso, mas depois de mudar esse código, isso parece muito mais legível e compreensível. Beco sem saída evitado. Ponto I ' O que estou tentando fazer é que mesmo alguém que seja completamente novo no OO possa produzir um código decente, desde que ele tenha um mentor com mais experiência, para lembrá-lo de que "design evolucionário" não é a mesma coisa que "sem design". E mesmo algumas de suas classes "mais complexas" não são tão assustadoras, porque cada classe não tem tanta responsabilidade (ou seja, não há tanto código), então o pior fica pior, se essa classe "acabar", nós jogue-o fora e escreva uma classe de substituição que tenha a mesma interface pública (até agora nunca vi necessidade dessa contingência em nada que escrevemos e tenho feito revisões de código duas vezes por semana).
Como observação final, eu também acredito firmemente nos documentos de design (pelo menos para as condições de negócios da minha equipe atual), mas o objetivo principal dos nossos documentos de design é a Memória Organizacional ; portanto, documentos reais são gravados após a produção do código e refatorado. Antes da codificação, geralmente temos uma fase de design rápida (às vezes não tão rápida), onde esboçamos aulas sobre guardanapos / mspaint / visio e eu sempre lembro às pessoas que essa fase produz um caminho a seguir, não um blueprint e quando eles começam a codificar, qualquer coisa que não faça sentido deve ser mudada. Mesmo com esses lembretes, os novatos tendem a tentar adaptar o código ao design original, por mais natural que pareça para eles. Isso geralmente aparece nas revisões de código.
Dang, eu escrevi muito. Me desculpe por isso.
Eu diria que o fenômeno do "design beco sem saída" é ortogonal aos métodos ágeis. O que quero dizer é que é possível fazer cascata, gastar muito tempo com um design (ruim). Depois gaste muito tempo implementando-o apenas para se encontrar em um beco sem saída.
Os métodos ágeis devem ajudá-lo a descobrir mais cedo que você fez más escolhas de design. A razão para isso é que sua lista de pendências deve ter os itens de maior valor para o cliente feitos primeiro e você deve se concentrar em fornecer incrementos úteis do software. Se o seu design permitir que você ofereça alto valor e utilidade, já é bom para algo :-) Por outro lado, você pode ter um design ruim em uma situação em que você não saiba por muitos anos que esse design não pode oferecer por muitos anos qualquer valor e utilidade - tudo o que você tem é a ilusão de ser um bom design. Como dizem, a prova está no pudim.
O lado oposto é que, mesmo em métodos ágeis, é importante ter uma visão viável para o design do sistema que direciona as decisões de iteração para iteração. Acho que Ken Schwabber disse algo como se você tiver uma equipe de desenvolvedores terríveis, eles produzirão software ruim de forma consistente, iteração por iteração. Agile significa simplesmente não gastar muito tempo adiantado, porque você é limitado no que pode aprender ou imaginar antes de começar a implementar ( e os requisitos também mudam). No entanto, existem algumas situações em que você precisa fazer um trabalho inicial (por exemplo, pesquisa) e depois precisa fazer isso.
Como você evita becos sem saída?
Eu diria principalmente antecipando requisitos futuros. Isso é algo que você obtém com experiência e familiaridade com projetos / produtos similares. Essa antecipação é parcialmente o que ajuda você a implementar um bom design, porque você se pergunta muitas perguntas "e se" sobre o seu sistema atual. Para mim, este é o componente crítico. Técnicas como OO estão simplesmente ajudando você quando você já sabe o que está fazendo.
O que você faz se tiver um beco sem saída?
A "beco sem saída" não é diferente de qualquer outro bloco técnico que você vai bater durante o desenvolvimento de qualquer coisa que é nova. A primeira coisa a entender é que realmente não existem "becos sem saída" que forçam você a voltar completamente. No mínimo, seu aprendizado até este ponto é o que permite avançar, para que o esforço não seja desperdiçado. Quando você atinge um beco sem saída, você tem um problema . O problema é o que precisa ser alterado para atender a algum requisito novo (ou antigo) e como otimizar essa alteração. Tudo o que você precisa fazer agora é resolver esse problema. Seja grato por este ser um software e não, por exemplo, um projeto de avião, porque a mudança é muito mais fácil. Identifique os problemas, corrija-os == refator == engenharia de software. Às vezes, muito trabalho está envolvido ...
Se você usa o Scrum, essa alteração deve naturalmente ser derivada das histórias do usuário (o que o usuário obtém dessa alteração?). O processo começaria a partir de uma história que não pode ser facilmente acomodada pelo design atual (oops) e ocorreria uma discussão com o proprietário do produto sobre como desmembrar essa história. Você continua aplicando princípios ágeis por meio dessa mudança.
Alguns grandes requisitos famosos mudam do mundo do SO que me vêm à mente:
Qualquer que seja a maneira como você olha para eles, eles dão muito trabalho. O design original quase certamente não levou em conta a possibilidade de isso acontecer (ou seja, a portailidade não era um grande requisito). Se o design foi OO ou não, provavelmente também não é um fator importante. Em um bom design, as partes específicas da plataforma seriam um pouco isoladas e o trabalho seria mais fácil.
fonte
Refatoro meus projetos permanentemente e também uso diagramas de classes UML. Quero dizer que crio um ou mais diagramas de classes por pacotes. Cada diagrama é salvo na raiz do pacote. Cada classificador UML possui um próprio ID, que é mapeado para o Java ID relacionado. Isso significa que, quando abro meu diagrama, ele é atualizado automaticamente para as alterações de refatoração de código mais recentes. Também posso alterar diretamente os diagramas de classe no nível gráfico e todo o meu projeto é refatorado imediatamente. Funciona muito bem, mas nunca substituirá humano. Meu diagrama de classes UML também é apenas uma visualização gráfica do meu código. É muito importante não misturar código e modelo como o EMF eclipse, pois assim que a refatoração é feita, as informações do modelo também são perdidas. Eu nunca uso o gerador de código Model Driven Development porque isso é inútil. Eu não'
Dito isto, ter mais de 100 diagramas de classe representando todos os detalhes da estrutura do meu projeto e cheio de notas em todos os lugares é realmente útil. Eu só crio diagramas de classe para projetos, porque geralmente os desenvolvedores não têm tempo para aprender ou usar outros diagramas. Os diagramas de classe também são muito bons porque são atualizados automaticamente. Os diagramas de classes podem ser criados após o código apenas revertendo um pacote e adicionando notas. É rápido e sempre preciso e 100% iterativo.
Não confunda entre desenvolvimento orientado a modelo, que é um código que gera modelo e geralmente usa UML como apresentação gráfica com diagramas de classes UML atualizados a partir do código. Somente o código sincronizado UML tem um valor real para mim se várias iterações.
Desculpe demorar tanto, mas acho que devemos dar uma segunda chance aos diagramas de classes UML, se usados apenas como uma visualização gráfica do nosso projeto. Isso significa que a UML cobre o projeto completo e possui um modelo único composto por diagramas de classe grandes que representam o projeto completo. Seria ridículo ter centenas de visualizações pequenas e um modelo para cada visualização em um projeto com centenas de visualizações :-)
fonte
Cheguei a um beco sem saída com meu código e outros códigos devido a um design incorreto, mudança de direção etc. Também vi muitos outros encontrarem esse problema. O grande erro (pelo menos me parece um erro) é o desejo imediato de jogar fora o código de trabalho e reimplementar tudo do zero.
Abordei cada caso da mesma maneira que parecia funcionar bem:
Custos:
Benefícios:
fonte
Há cerca de um mês ou dois, nosso projeto atual ficou um pouco parado devido a algumas decisões ruins de design (e falta de muito design em um só lugar), com o estilo de desenvolvimento SCRUM.
Nossa solução (e o que eu acredito ser a solução padrão para o SCRUM) foi dedicar um sprint inteiro (~ 2 semanas) a nada além de refatoração. Nenhuma nova funcionalidade foi adicionada durante esse período, mas pudemos pensar na atual base de código e projetar um sistema muito melhor para o que estávamos fazendo.
Agora superamos esse obstáculo e adicionamos novos recursos novamente.
fonte
A chave para limitar o custo das alterações de design é manter o código o mais SECO possível. Isso conduzirá a maior parte do código do aplicativo a um nível muito alto, onde a maior parte do código expressa diretamente a intenção e relativamente pouco especifica o mecanismo. Se você fizer isso, as decisões de design terão a menor expressão possível no código e as alterações de design terão o menor custo possível.
fonte
A chave para evitar becos sem saída no design é reconhecer o mais cedo possível quando seu design precisar mudar e alterá-lo. Os maiores problemas não surgem evoluindo continuamente seu design, mas recusando - se a evoluí-lo até que ele seja um grande problema.
Como exemplo, o Netflix possui um recurso de perfil, no qual diferentes membros da família podem cobrar no mesmo plano, mas têm filas separadas. Alguns anos atrás, eles anunciaram que precisariam cancelar esse recurso, porque apenas 10% dos usuários o usavam, mas devido ao hackeamento na implementação, estavam consumindo uma quantidade excessiva de trabalhos de manutenção. Depois de um tumulto, eles morderam a bala e fizeram um reprojeto caro para manter esses clientes.
Tenho certeza de que alguns engenheiros reconheceram um design abaixo do ideal quando adicionaram esse recurso pela primeira vez. Se eles tivessem mudado na época, não teria sido tão importante.
fonte
Não foi Fred Brooks quem disse algo como "Planeje jogar o primeiro fora"? Não se sinta muito triste, os designs sem saída também aparecem em projetos que tentam fazer todo o design antecipadamente. As redesigns acontecem em todos os tipos de desenvolvimento, seja porque foi um projeto impraticável desde o início (os últimos 20% que são encobertos - "o diabo está nos detalhes") ou porque um cliente muda seu foco. Não há necessidade real de alarmes, não fique muito preocupado.
fonte