Como você gerencia um salto de complexidade?

13

Parece uma experiência pouco frequente, mas comum, que às vezes você está trabalhando em um projeto e de repente algo aparece inesperadamente, lança uma chave de boca maciça nas obras e aumenta muito a complexidade.

Por exemplo, eu estava trabalhando em um aplicativo que falava com serviços SOAP em várias outras máquinas. Eu criei um protótipo que funcionou bem, depois desenvolvi um front end regular e, geralmente, colocava tudo em funcionamento de uma maneira agradável, bastante simples e fácil de seguir. Funcionou muito bem até que começamos a testar em uma rede mais ampla e, de repente, as páginas começaram a atingir o tempo limite, pois a latência das conexões e o tempo necessário para realizar cálculos em máquinas remotas resultaram em solicitações de tempo limite aos serviços de sabão. Aconteceu que precisávamos alterar a arquitetura para gerar solicitações em seus próprios encadeamentos e armazenar em cache os dados retornados, para que pudessem ser atualizados progressivamente em segundo plano, em vez de realizar cálculos em uma solicitação por solicitação.

Os detalhes desse cenário não são muito importantes - na verdade, não é um ótimo exemplo, pois era bastante previsível e as pessoas que escreveram muitos aplicativos desse tipo para esse tipo de ambiente podem tê-lo antecipado - exceto que ilustra uma maneira de pode-se começar com uma premissa e modelo simples e de repente ter uma escalada de complexidade até o desenvolvimento do projeto.

Quais estratégias você tem para lidar com esses tipos de mudanças funcionais cuja necessidade surge - geralmente como resultado de fatores ambientais e não de alterações nas especificações - posteriormente no processo de desenvolvimento ou como resultado de testes? Como você equilibra entre evitar os riscos prematuros de otimização / YAGNI / superengenharia de projetar uma solução que atenue problemas possíveis, mas não necessariamente prováveis , em vez de desenvolver uma solução mais simples e fácil, que provavelmente seja tão eficaz, mas não incorpore a preparação para toda eventualidade possível?

Editar: a resposta de Crazy Eddie inclui "você absorve e encontra a maneira mais barata de implementar a nova complexidade". Isso me fez pensar em algo implícito na pergunta, mas não levantei especificamente.

Depois de acertar o solavanco e incorporar as alterações necessárias. Você faz o que manterá o projeto o mais próximo possível do cronograma, mas pode afetar a capacidade de manutenção ou você volta à sua arquitetura e a refaz em um nível mais detalhado, que pode ser mais sustentável, mas atrasa tudo durante o desenvolvimento?

glenatron
fonte

Respostas:

8

O que me vem à cabeça ao ler isso é o ditado ágil: lide com as tarefas mais arriscadas e / ou menos bem compreendidas primeiro no ciclo de vida do projeto . Ou seja, tente montar um esqueleto funcional do projeto o mais cedo possível, para provar que o conceito funciona. Isso, por sua vez, também permite executar qualquer tipo de teste cruel para detectar se a arquitetura realmente cumpre sua promessa em circunstâncias da vida real. Além disso, se houver alguma tecnologia / plataforma / ferramenta nova e desconhecida incluída na solução, leve isso também cedo.

Se a arquitetura principal estiver correta, as funcionalidades individuais poderão ser adicionadas e testadas de forma incremental e refatoradas quando necessário, com custo relativamente menor. Precisando mudar a arquitetura é o grande risco, com o qual devemos lidar antecipadamente. Isso fornece um feedback rápido: na pior das hipóteses, se todo o conceito desmoronar, nós o conhecemos cedo e podemos abortar o projeto com perda mínima.

Péter Török
fonte
6

Seu exemplo tocou em alguns dos aspectos mais desafiadores da programação, como computação distribuída e programação simultânea , que estão se tornando mais amplamente utilizados e tornando os programadores cada vez mais difíceis.

Até mesmo a programação "normal" (thread único em uma máquina) é tão complexa para qualquer programa não convencional, que é preciso muita habilidade e anos de experiência para ser bom - mas ainda longe de ser "resolvido". Mesmo neste nível, as complexidades, principalmente devido à explosão combinatória , excedem em muito a capacidade do cérebro humano de compreender e compreender completamente. Pensar de outra maneira é tolice.

A computação distribuída e a programação simultânea adicionam mais duas dimensões ao tamanho do espaço da "complexidade", que cresce pelo menos em cúbicos (sp?) (N ^ 3) em comparação à programação "normal". Apenas por exemplo, pense em alguns novos conjuntos de problemas e falácias com os quais temos de lidar. Até brincar com uma ideia de que você pode entender interconexões e efeitos colaterais nessa escala é risível.

Claramente não tenho balas de prata, mas tenho certeza de que o maior erro que podemos cometer é pensar que você entendeu tudo e resolveu.

Algumas idéias sobre como lidar com tudo isso, além de outras respostas já abordadas:

  • Grande humildade
  • Aceite que seu sistema / programa seja imperfeito, impermanente e incompleto .
  • Prepare-se para erros
  • Abrace a mudança
  • Planejar redundância
  • Pense em provas futuras
  • Observe (ou estude) a biologia ou a sociologia como os sistemas complexos se comportam
  • Tente o máximo para evitar um estado mutável. Escolha protocolos sem estado (como REST e HTTP).
  • A programação funcional pode aliviar parte da dor

Eu acho que eu poderia continuar e continuar. Assunto muito interessante :)

Maglob
fonte
Observe (ou estude) a biologia ou a sociologia como os sistemas complexos se comportam - vamos lá. O restante de sua resposta foi sólido, mas isso tem uma aplicação periférica ao problema descrito.
Jim G.
1
@ Jim G. Talvez. A biologia não ajudará a otimizar seus loops, mas se você quiser criar novas perspectivas, insights ou abstrações eficazes (no desenvolvimento de software), isso ajudará a sair da caixa de areia. Argumentando que a biologia (ou a sociologia) não tem nada a ver com programação, apenas alguns saltos de argumentar que OOP ou padrões de design não têm nada a ver com programação. Por exemplo: OOP : biologia -> Alan Kay -> OOP / Smalltalk. Ou Padrões de design : sociologia -> design urbano -> Christopher Alexander -> Uma linguagem de padrões -> Padrões de design.
Maglob
@Jim G. Cont. Algumas citações, Alan Kay: "Pensei em objetos como células biológicas e / ou computadores individuais em uma rede, capazes apenas de se comunicar com mensagens", e Wikipedia: "[Design Pattern] A idéia foi introduzida pelo arquiteto Christopher Alexander em o campo da arquitetura [1] e foi adaptado para várias outras disciplinas, incluindo ciência da computação "
Maglob 11/11/11
Bem. Estou dando um +1 para Tente o máximo para evitar o estado mutável e outras pepitas. O que quero dizer é que, se o seu gerente lhe incumbisse de reduzir a complexidade, você certamente aplicaria a navalha da Occam ao problema e começaria a trabalhar. Eu não acho que você ou qualquer outra pessoa "procuraria a biologia" para obter ajuda com o problema imediato.
Jim G.
2

Não concordo com o espírito da resposta de @ Péter Török, porque pressupõe que uma equipe (ou indivíduo) possa necessariamente prever os itens mais arriscados no início do ciclo de vida do projeto. Por exemplo, no caso do OP, a equipe não podia prever a complexidade crescente associada à solução multithread até que suas costas estivessem contra a parede.

A pergunta do OP é boa e mostra um problema que muitas lojas de desenvolvimento de software têm.

Aqui está como eu lidaria com o problema:

  1. Siga o conselho de Fred Brooks e organize seus desenvolvedores como uma equipe de cirurgia .
  2. Escolha um mestre-cirurgião sábio e "benevolente" que possa: A) Conquistar a confiança e o respeito de seus colegas; e B) Tomar decisões difíceis em tempo hábil.
  3. Espere que o cirurgião-mestre reduza a complexidade no front-end e no back-end do processo de desenvolvimento.

Mais sobre o ponto 3:

  1. O cirurgião-mestre deve fazer um esforço consciente para propor a solução mais simples que funcionará. Anos de experiência significativa devem colocar o cirurgião-mestre em posição de fazê-lo.
  2. A organização mais ampla, que é a superior do cirurgião-mestre, deve fornecer à equipe tempo e recursos suficientes para reduzir a complexidade após a data de envio. Isso permitirá que a equipe de desenvolvimento envie o código em tempo hábil e execute o kaizen para reduzir a complexidade continuamente.
Jim G.
fonte
IMHO no caso do OP, eles deveriam ter começado a testar anteriormente, para descobrir como (e se) sua arquitetura funciona em circunstâncias da vida real. Btw, sugerindo ter um "cirurgião mestre", você parece implicar que, basicamente, não são as pessoas que podem prever os riscos técnicos do projeto - o ponto exato que você afirmam discordar.
Péter Török
@ Péter Török: ... Ao sugerir um "cirurgião mestre", você parece implicar basicamente que existem pessoas que podem prever os riscos técnicos do projeto : Não, não sou. Estou dizendo que essas duas pessoas são: A) Mais adequadas para evitar completamente a complexidade em primeiro lugar; e B) Mais adequado para desenterrar uma equipe da complexidade após o envio do código.
10777 Jim G.
IMHO estamos falando sobre a mesma coisa. A experiência que ajuda seu "cirurgião mestre" a escolher a solução mais simples que pode funcionar é construída a partir de memórias de projetos e soluções anteriores e do conhecimento de qual solução funcionou (ou não) em qual caso específico. Em outras palavras, ele examina as soluções aplicáveis ​​para problemas específicos e avalia os possíveis benefícios e riscos de cada um. É isso que o ajuda a escolher o caminho certo para a situação atual, evitando os caminhos mais arriscados .
Péter Török
1
Isso me lembra uma citação do falecido e ótimo treinador de cavalos Ray Hunt: "Como você obtém um bom julgamento? Experiência. Como você obtém experiência? Um mau julgamento".
glenatron
1

Código para interfaces

Ao escrever uma nova funcionalidade em interface com outra funcionalidade, faça um limite na forma de uma interface (o tipo Java) pela qual todas passam. Isso vai

  1. garantir que você tenha controle total sobre quais funcionalidades são usadas
  2. permitem que você tenha várias implementações da mesma funcionalidade.
  3. mantenha a complexidade geral baixa porque os módulos estão apenas pouco conectados ao invés de estarem totalmente entrelaçados.

fonte
0

pode-se começar com uma premissa e modelo simples e de repente ter uma escalada de complexidade até o desenvolvimento do projeto

Não é surpreendente.

Isso é desenvolvimento de software. Se você não está inventando algo novo, está baixando uma solução já comprovada.

Há pouco meio termo.

Se você está inventando algo novo, deve haver pelo menos um recurso que você não entende completamente. (Para entendê-lo completamente , você precisaria ter uma implementação funcional, que você usaria apenas.)

Como gerenciar isso?

  1. Tenha expectativas realistas. Você está inventando algo novo. Não devem ser partes que você não entende.

  2. Tenha expectativas realistas. Se parece funcionar bem na primeira vez, você ignorou alguma coisa.

  3. Tenha expectativas realistas. Se fosse simples, alguém teria feito isso primeiro e você poderia simplesmente fazer o download dessa solução.

  4. Tenha expectativas realistas. Você não pode prever o futuro muito bem.

S.Lott
fonte
2
Espere, então o que você está dizendo é: Tem expectativas realistas?
glenatron
0

Design e código com obsolescência em mente. Suponha que o que você codifique hoje precisará ser cortado e substituído amanhã.

Ian
fonte
0

O ambiente deve fazer parte da especificação. Assim, uma mudança no ambiente é uma mudança na especificação. Se, por outro lado, você baseou seu protótipo e design em um ambiente diferente do que estava na especificação, cometeu um erro tolo. De qualquer forma, você absorve e encontra a maneira mais barata de implementar a nova complexidade.

Edward Strange
fonte
0

Como na maioria dos problemas de programação, isso depende , na minha opinião. Essa questão é tão intrínseca ao trabalho criativo, que você não deve esquecer que as falhas vão acontecer, e tudo bem . A programação é um problema grave e você normalmente não sabe a solução certa para o problema até que você já o tenha resolvido.

No entanto, existem vários fatores locais específicos que podem entrar em jogo aqui, como:

  • Os objetivos para este sistema. É algo único? Você pretende manter esse sistema funcionando a médio e longo prazo?

Para coisas de curto prazo, pode não valer a pena pensar nisso mais do que o suficiente para fazê-lo funcionar. A refatoração é cara e é algo que não cria valor final imediato para o usuário. Contudo, não há praticamente nenhum caso em que não seja um software descartável absoluto, em que é tão curto que não vale a pena melhorar seu design. É muito mais importante entender o que você fez e corrigi-lo rapidamente do que terminar agora. Se for a longo prazo, provavelmente será recompensado eventualmente (e possivelmente muito mais cedo do que todos os envolvidos pensam), ou o inverso (não fazê-lo causará dor muito em breve, em vez de "quando tivermos que consertá-lo"). Estou quase tentado a dizer "sempre reserve um tempo para melhorar", mas há alguns casos em que isso não é possível.

  • Os objetivos da equipe. É mais um tipo de "faça agora, a qualquer custo" ou um tipo de coisa "vamos fazer certo"?

Isso deve influenciar enormemente suas decisões. Sua equipe apoiará essa decisão fornecendo recursos para a reformulação ou exigirá a solução rápida a ser feita agora. Na minha opinião, se você achar que a equipe está empurrando você na direção errada de forma consistente, é uma enorme bandeira vermelha. Eu já vi esse tipo de coisa acabar em um cenário em que há constante extinção de incêndio, onde nunca há tempo para redesenhar, porque você está sempre corrigindo os problemas que seu mau design cria. No entanto, também pode haver um meio termo: "fita adesiva" agora, corrija o mais rápido possível (mas realmente faça isso).

  • Sua compreensão do problema. Por que a solução anterior não funcionou?

Muito importante. Pense sobre qual é o erro ou problema e por que está acontecendo. Esse tipo de situação é uma grande oportunidade para encontrar suposições, restrições e interações falhas (ou ausentes). Em geral, sempre favoreça entender melhor o seu problema em vez de resolvê-lo. Esta é provavelmente a sua maior defesa contra a YAGNI / superengenharia. Se você entender o seu problema bem o suficiente, então você vai resolver isso e não outros problemas.

Finalmente, tente construir as coisas da maneira certa . Não estou falando dos erros e problemas que você enfrenta quando entende mais sobre o problema ou sobre o erro humano inerente. Não quero dizer "não cometa erros e aperfeiçoe-o da primeira vez" - isso é impossível. Quero dizer, tente gerenciar bem a complexidade no seu trabalho diário, conserte janelas quebradas, mantenha o mais simples possível, melhore seu código e seu pensamento o tempo todo. Dessa forma, quando (e não se) mudar à sua porta, você poderá recebê-lo de braços abertos em vez de uma espingarda.

Juan Carlos Coto
fonte