O que você faz se atingir um beco sem saída de design em métodos evolutivos como Agile ou XP?

11

Enquanto eu lia o famoso post de Martin Fowler, Is Design Dead? , uma das impressões impressionantes que recebi é que, como na metodologia ágil e na programação extrema, o design e a programação são evolutivos, sempre há pontos em que as coisas precisam ser refatoradas.

Pode ser possível que, quando o nível de um programador seja bom e ele entenda as implicações do design e não cometa erros críticos, o código continue a evoluir. No entanto, em um contexto normal, qual é a realidade básica nesse contexto?

Em um dia normal, dado que algum desenvolvimento significativo entra no produto, e quando ocorrem mudanças críticas nos requisitos, não é uma restrição que, quanto desejamos, aspectos fundamentais do projeto não possam ser modificados? (sem jogar fora grande parte do código). Não é bem provável que se chegue a um beco sem saída com mais melhorias possíveis no design e nos requisitos?

Não estou defendendo nenhuma prática não-ágil aqui, mas quero saber de pessoas que praticam métodos de desenvolvimento ágil, iterativo ou evolutivo, quanto às suas experiências reais.

Você já alcançou esses becos sem saída ? Como você conseguiu evitá-lo ou escapá-lo? Ou existem medidas para garantir que o design permaneça limpo e flexível à medida que evolui?

Dipan Mehta
fonte

Respostas:

17

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.

DXM
fonte
1
+1 Valeu a pena por cada palavra. Eu me deparei com essas situações como você descreveu e depois que o prazo é cumprido, peço que limpem (leia refatorar) mais do que eu acho que é senso comum de design. Mas muitas vezes as pessoas acham a mesma pergunta repetida - Por que estou fazendo a mesma coisa novamente? Acho que agora tenho a resposta - se você precisar de um tempo mais rápido para o mercado e permitir que o design evolua, a refatoração não é uma compensação aos seus pecados antigos, mas, na verdade, é uma coisa legítima a se fazer.
Dipan Mehta 12/11/11
Sim, você escreveu muito, mas foi uma coisa boa. Realmente gostei de ler :).
Radu Murzea
11

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.

Guy Sirton
fonte
Na verdade, as primeiras versões do Windows NT implementavam uma "Camada de extração de hardware" e suportavam o DEC Apha e o x86. Como ninguém nunca comprou máquinas DEC alfa, isso foi descartado silenciosamente. Eu imagino que essa "independência da máquina" ainda exista em um formato vestigial na versão atual, portanto uma porta ARM pode não ser tão difícil.
James Anderson
3

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 :-)

UML_GURU
fonte
1
+1 para mostrar a ideia do código postal UML! Curiosamente, nunca voltamos aos documentos dos diagramas após o código!
Dipan Mehta 12/11/11
Sim, esse é exatamente o problema com o desenvolvimento orientado a modelo associado à UML. Você gera documentação e nunca a utiliza se houver alguma modificação no projeto. A mesclagem de modelo e código nos permite usar a UML e alterá-la quantas vezes for necessário.
UML_GURU
3

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:

  • identificar por que o design atual não está funcionando
  • criar um novo design e um plano de transição
  • marcar o código que não será usado como obsoleto
  • implementar apenas o que eu preciso para o novo design, a fim de satisfazer a necessidade imediata
  • remova o código que o novo código torna obsoleto

Custos:

  • mais complexidade devido a 2 implementações na base de código ao mesmo tempo
  • custo total mais alto por recurso / correção de bug do que fazer uma reformulação / reimplementação completa, assumindo bons casos de uso e testes

Benefícios:

  • pelo menos uma ordem de magnitude menor a risco, porque você ainda tem o código / design de trabalho antigo
  • mercado mais rápido para quaisquer recursos de 1 a n-1 que não exijam uma reformulação completa
  • se o produto / código acabar morrendo antes de você concluir a reformulação completa, você terá economizado a diferença no tempo de desenvolvimento
  • abordagem iterativa e todos os benefícios
dietbuddha
fonte
2

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.

Izkata
fonte
Este é outro grande atrito a ser encontrado. É preciso dizer ao cliente - que agora estamos construindo a mesma coisa - de recursos que não são novidade, após a entrega da primeira versão! Com que frequência e com frequência (se é que alguma vez) você pode contar isso ao cliente? ou como você explica?
Dipan Mehta
Você tem duas opções básicas: primeiro, basta dizer a verdade simples - que, embora o que você tem funcione seja uma bagunça e precise ser arrumado para avançar, ou segundo, você diz que está construindo infraestrutura para suportar o desenvolvimento em andamento ( o que não é menos verdadeiro, mas é girado para ser um pouco mais positivo). Você pode encerrar os dois dizendo que, para oferecer a funcionalidade, incorremos em uma dívida técnica que agora precisa ser paga.
Murph
@Dipan Mehta: Bem, suponha que um cliente queira uma casa de dois andares. Você o projeta e entrega. Então eles querem ter quatro andares adicionais. Você diz, bem, eu tenho que gastar esse tempo apenas para tornar o edifício atual mais robusto, de modo a manter quatro andares adicionais. Portanto, não acho que isso seja um problema para o cliente se o plano original incluir apenas dois andares. Se seis andares foram planejados desde o início, sim, pode ser um problema informar o cliente.
Giorgio
@DipanMehta Também temos um pouco de sorte, pois os clientes não sabem necessariamente sobre esse projeto. É uma atualização para um produto que eles usam atualmente, com uma data de conclusão semi-vaga por volta do final deste ano. Então, eles nem sequer precisa de saber sobre um atraso de refatoração;) (O gerente de lidar com a maioria das decisões de design é in-house)
Izkata
2

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.

Kevin Cline
fonte
2

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.

Karl Bielefeldt
fonte
1

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.

anon
fonte