A solução deve ser o mais genérica possível ou o mais específica possível?

124

Digamos que eu tenha uma entidade que tenha o atributo "type". Pode haver mais de 20 tipos possíveis.

Agora me pedem para implementar algo que permita alterar o tipo de A-> B, que é o único caso de uso.

Então, devo implementar algo que permita alterações arbitrárias de tipo, desde que sejam válidas? Ou devo SOMENTE permitir que ele mude de A-> B de acordo com o requisito e rejeitar qualquer outra alteração de tipo, como B-> A ou A-> C?

Eu posso ver os prós e os contras de ambos os lados, onde uma solução genérica significaria menos trabalho no caso de surgir um requisito semelhante no futuro, mas também significaria mais chances de dar errado (embora 100% controlemos o chamador neste momento). ponto).
Uma solução específica é menos suscetível a erros, mas requer mais trabalho no futuro, se um requisito semelhante surgir.

Eu continuo ouvindo que um bom desenvolvedor deve tentar antecipar a mudança e projetar o sistema para que seja fácil estender no futuro, o que soa como uma solução genérica.

Editar:

Adicionando mais detalhes ao meu exemplo não tão específico: A solução "genérica" ​​neste caso requer menos trabalho que a solução "específica", pois a solução específica requer validação no tipo antigo e no novo tipo, enquanto a solução genérica só precisa validar o novo tipo.

LoveProgramming
fonte
97
Essa é uma pergunta interessante. Eu tive uma discussão sobre algo assim com meus avós (um programador de temporizadores muito, muito antigo) e a resposta dele foi algo como "a coisa mais genérica que resolve seu problema específico", que se resume a uma fantasia " Depende". Declarações gerais no desenvolvimento de software raramente funcionam - as coisas devem ser tomadas sempre caso a caso.
T. Sar
2
@ T.Sar adicione isso como resposta e eu voto a favor :-) #
Christophe
4
O que é uma "solução genérica" ​​na sua opinião? Além disso, esclareça o que você entende por “único caso de uso”. Você quer dizer que apenas uma transição de A-> B é permitida ou apenas essa transição é especificada e não seria uma condição de erro fazer a transição de qualquer outro estado para outro estado. Como desenvolvedor, você precisa pedir ao autor do caso de uso que esclareça. Se nenhuma outra transição for permitida e seu código permitir, independentemente de quem controla o chamador, seu código falhou em atender aos requisitos.
RibaldEddie
5
O princípio de que se X é uma boa idéia, então apenas X o tempo todo deve ser ideal, parece ter um apelo particular aos desenvolvedores (ou pelo menos um grupo vocal entre eles), mas considere isso: as regras práticas da programação são como provérbios, em que muitas vezes você pode encontrar um par com implicações opostas. Este é um sinal de que você deve usar seu julgamento (como preconizado pelas respostas aqui) e cuidado com o dogma.
sdenham
2
A Generalização prematura pode causar tanta azia quanto a Otimização prematura - você acaba escrevendo muitos códigos que talvez nunca sejam usados. Resolvido primeiro para o problema específico, depois generalize conforme a necessidade.
John Bode

Respostas:

296

Minha regra de ouro:

  1. Na primeira vez em que encontrar o problema, resolva apenas o problema específico (este é o princípio YAGNI )
  2. Na segunda vez que encontrar o mesmo problema, considere generalizar o primeiro caso, se não der muito trabalho
  3. depois de ter três casos específicos em que seria capaz de usar a versão generalizada, você deveria realmente começar a planejar a versão generalizada - agora, você deve entender o problema o suficiente para poder generalizá-lo.

Obviamente, essa é uma diretriz e não uma regra rígida: a resposta real é usar seu melhor julgamento, caso a caso.

Daniel Pryden
fonte
1
Você pode explicar o que é YAGNI?
Bernhard
7
@ Bernhard Você não precisa disso . Um princípio de design de software muito útil.
Angew
4
"Na thing1, thing2, pense em talvez usar uma matriz. Na thing1, thing2, thing3, quase certamente use a thing[]matriz." é uma regra prática semelhante para decidir entre várias variáveis ​​ou uma única matriz.
Joker_vD
15
Outra maneira de declarar a regra 1: "Você não pode generalizar uma coisa."
Wayne Conrad
2
@SantiBailors: Como o Dr. Brown disse em sua resposta, geralmente superestimamos bastante a quantidade de esforço desperdiçado que poderíamos gastar construindo o primeiro a jogar fora. No The Mythical Man-Month , Fred Brooks diz: "Planeje jogar um fora - você irá, de qualquer maneira". Dito isto: se você encontrar imediatamente mais de um uso para alguma coisa - por exemplo, ao receber um conjunto de requisitos em que claramente precisará resolver o mesmo problema mais de uma vez -, você já terá mais de um caso para generalizar de, e isso é totalmente bom e não entra em conflito com a minha resposta.
Daniel Pryden
95

Uma solução específica [...] requer mais trabalho no futuro, se requisitos semelhantes forem necessários

Ouvi esse argumento várias dezenas de vezes e, segundo minha experiência, ele regularmente se mostra uma falácia. Se você generalizar agora ou mais tarde, quando surgir o segundo requisito semelhante, o trabalho será quase o mesmo no total. Portanto, não faz sentido investir esforços adicionais na generalização, quando você não sabe que esse esforço será recompensado.

(Obviamente, isso não se aplica quando uma solução mais geral é menos complicada e requer menos esforço do que uma solução específica, mas, para minha experiência, são casos raros. Esse tipo de cenário foi editado posteriormente na pergunta e não é o mais uma é a minha resposta).

Quando o "segundo caso semelhante" aparecer, é hora de começar a pensar em generalizar. Será muito mais fácil generalizar corretamente , pois esse segundo requisito fornece um cenário em que você pode validar se tornou genérico as coisas certas. Ao tentar generalizar apenas para um caso, você está filmando no escuro. As chances são altas de generalizar demais certas coisas que não precisam ser generalizadas e perder outras partes que deveriam. E quando surge um segundo caso e você percebe que generalizou as coisas erradas, tem muito mais trabalho a fazer para consertar isso.

Portanto, recomendo adiar qualquer tentação de fazer as coisas "por precaução". Essa abordagem só levará a mais esforços de trabalho e manutenção quando você perder a ocasião de generalizar três, quatro ou mais vezes e depois tiver uma pilha de código de aparência semelhante (tão duplicada) para manter.

Doc Brown
fonte
2
Para mim, essa resposta não faz sentido no contexto do OP. Ele está dizendo que passará menos tempo generalizando agora e menos tempo no futuro, a menos que, no futuro, exista uma necessidade de que a implementação seja específica. Você está argumentando contra "investir esforços adicionais na generalização", enquanto o OP investirá esforços adicionais apenas se não generalizarem. (think) .... estranho: $
msb
2
@ msb: esse contexto foi adicionado depois que eu escrevi minha resposta e vai ao contrário do que o OP estava dizendo antes, especialmente na parte que citei. Veja minha edição.
Doc Brown
2
Além disso, a solução generalizada será mais complicada; portanto, quando você precisar adaptá-la para o segundo caso, levará muito mais tempo para entendê-la novamente.
AndreKR
4
Doc, um aparte, mas nunca teria imaginado que você era um falante não-nativo. Suas respostas estão sempre bem escritas e bem argumentadas.
precisa saber é o seguinte
2
Imagino seu sotaque quando estiver lendo suas respostas futuras. :-)
user949300
64

TL; DR: depende do que você está tentando resolver.

Eu tive uma conversa semelhante com meus vovôs sobre isso, enquanto conversávamos sobre como Func e Action em C # são impressionantes. My Gramps é um programador de cronômetros muito antigo, que usa códigos-fonte desde que o software foi executado em computadores que ocupavam uma sala inteira.

Ele mudou de técnico várias vezes em sua vida. Ele escreveu código em C, COBOL, Pascal, BASIC, Fortran, Smalltalk, Java e acabou iniciando o C # como um hobby. Eu aprendi a programar com ele, sentado no colo dele enquanto eu era apenas um demônio, esculpindo minhas primeiras linhas de código no editor azul do SideKick da IBM. Quando eu tinha 20 anos, eu já havia passado mais tempo codificando do que tocando lá fora.

Essas são algumas das minhas lembranças, então com licença, se eu não for exatamente prático enquanto as reconto. Eu gosto um pouco desses momentos.

Foi o que ele me disse:


"Devemos procurar a generalização de um problema ou resolvê-lo no escopo específico, você pergunta? Bem, essa é uma ... pergunta."

Vovô fez uma pausa para pensar sobre isso por um breve momento, enquanto fixa a posição de seus óculos no rosto. Ele estava jogando um jogo de combinação 3 no computador, enquanto ouvia um LP do Deep Purple em seu antigo sistema de som.

"Bem, isso dependeria de qual problema você está tentando resolver", ele me disse. "É tentador acreditar que existe uma solução única e santa para todas as opções de design, mas não existe. A arquitetura de software é como queijo, você vê."

"... Queijo, vovô?"

"Não importa o que você pensa sobre o seu favorito, sempre haverá alguém que acha que é mau cheiro".

Eu pisquei em confusão por um momento, mas antes que eu pudesse dizer qualquer coisa, o vovô continuou.

"Quando você está construindo um carro, como você escolhe o material de uma peça?"

"Eu ... acho que depende dos custos envolvidos e do que a peça deve fazer, suponho."

"Depende do problema que a parte está tentando resolver. Você não fabricará um pneu de aço ou um para-brisa de couro. Você escolhe o material que melhor resolve o problema que tem em mãos. Agora, o que é um solução genérica? Ou uma específica? Para qual problema, para qual caso de uso? Você deve seguir uma abordagem funcional completa, para dar flexibilidade máxima a um código que será usado apenas uma vez? Você deve escrever um código frágil e especializado para uma parte do seu sistema que verá muitos usos e possivelmente muitas mudanças? As escolhas de design como essas são como os materiais que você escolhe para uma peça em um carro ou a forma do tijolo Lego que você escolhe para construir uma pequena casa . Qual é o melhor tijolo de Lego? "

O programador idoso pegou um pequeno modelo de trem Lego que ele tinha em sua mesa antes de continuar.

"Você só pode responder isso se souber o que precisa desse tijolo. Como diabos você saberá se a solução específica é melhor que a genérica, ou vice-versa, se você nem sabe qual é o seu problema?" tentando resolver? Você não pode ver além de uma escolha que não entende. "

"..Você acabou de citar The Matrix? "

"O que?"

"Nada, continue."

"Bem, suponha que você esteja tentando criar algo no Sistema Nacional de Faturas. Você sabe como essa API infernal e seu arquivo XML de trinta mil linhas se parecem por dentro. Como seria uma solução 'genérica' para criar esse arquivo? como? O arquivo está cheio de parâmetros opcionais, cheio de casos que somente ramos de negócios muito específicos devem usar. Na maioria dos casos, você pode ignorá-los com segurança. Você não precisa criar um sistema de fatura genérico se a única coisa que você ' Você sempre venderá sapatos. Crie um sistema para vender sapatos e faça com que ele seja o melhor sistema de faturas vendidas por sapatos já existentes. Agora, se você tivesse que criar um sistema de faturas para qualquer tipo de cliente, em um aplicativo mais amplo - ser revendido como um sistema de vendas genérico independente,por exemplo - agora é interessante implementar as opções que são usadas apenas para gás, comida ou álcool.Agora, esses são possíveis casos de uso. Antes eles eram apenas alguns casos hipotéticos de Não use e você não deseja implementar Não use casos. Não use é o irmão mais novo de Não precisa . "

Vovô colocou o trem de lego de volta em seu lugar e voltou ao seu jogo de combinar 3.

"Portanto, para poder escolher uma solução genérica ou específica para um determinado problema, você primeiro precisa entender o que diabos é esse problema. Caso contrário, você está apenas adivinhando, e adivinhar é o trabalho dos gerentes, não dos programadores. Como quase tudo em TI, depende. "


Então, aí está. "Depende". Essa é provavelmente a expressão de duas palavras mais poderosa quando se pensa em design de software.

T. Sar
fonte
14
Eu tenho certeza que há algo de útil nessa história, mas era demasiado doloroso de ler, desculpe
GoatInTheMachine
28
Sua primeira mensagem foi uma pequena anedota divertido - e é claro que é uma questão de opinião -, mas a menos que eu realmente como alguém estilo de escrita I encontrar longas histórias como esta realmente auto-indulgente e um pouco inadequado outside de um blog pessoal
GoatInTheMachine
16
@ T.Sar não ouça os inimigos, seu post é uma jóia de conselhos úteis de um guru. +1
LLlAMnYP
7
A parte "arquitetura de software é como queijo" foi simplesmente incrível. De fato, esta resposta é uma jóia!
Mathieu Guindon
3
Talvez esta resposta seja como queijo também? ;) Mas, por favor, "SmallTalk" deve ser "Smalltalk", e sim, eu estou sendo esse cara, desculpe.
fede s.
14

Primeiramente, você deve tentar prever se é provável que essa mudança ocorra - e não apenas uma possibilidade remota em algum lugar abaixo da linha.

Caso contrário, geralmente é melhor optar pela solução simples agora e estendê-la mais tarde. É bem possível que você tenha uma imagem muito mais clara do que é necessário então.

doubleYou
fonte
13

Se você está trabalhando em um domínio que é novo para você, a regra dos três que Daniel Pryden mencionou definitivamente deve ser aplicada. Afinal, como você deve criar abstrações úteis se for um novato nessa área? As pessoas costumam ter autoconfiança em sua capacidade de captar abstrações, embora raramente seja o caso. Na minha experiência, a abstração prematura não é menos má do que a duplicação de código. Abstrações erradas são realmente difíceis de entender. Às vezes, é ainda mais doloroso refatorar.

Há um livro abordando meu ponto de vista sobre um desenvolvedor de área desconhecida. É composto por domínios específicos com abstrações úteis extraídas.

Zapadlo
fonte
A questão é clara sobre um problema concreto.
RibaldEddie
4
A resposta é genérica o suficiente para resolver esse problema concreto. E a pergunta não é específica o suficiente para receber um recibo concreto: como você pode ver, nenhum detalhe de domínio é mencionado em uma pergunta. Mesmo os nomes de classe não são especificados (A e B não contam).
Zapadlo 30/11
7
"Uma resposta de fluxo de pilha deve ser a mais genérica possível ou a mais específica possível?"
Lyndon White
@LyndonWhite uma pergunta de stackoverflow deve ser a mais genérica possível (muito ampla!) Ou tão específica quanto possível (seja qual for o nome dessa falha!)? Ha.
4

Dada a natureza do corpo da sua pergunta, supondo que eu a entenda corretamente, na verdade eu vejo isso como uma questão de design dos recursos do sistema central, mais do que uma pergunta sobre soluções genéricas versus soluções específicas.

E quando se trata de recursos e capacidades do sistema central, os mais confiáveis ​​são os que não existem. Vale a pena errar do lado do minimalismo, especialmente considerando que geralmente é mais fácil adicionar funcionalidade centralmente, por muito tempo desejada, do que remover funcionalidades problemáticas, muito indesejadas, com inúmeras dependências, porque tornou o trabalho com o sistema muito mais difícil. do que precisa, levantando inúmeras questões de design a cada novo recurso.

De fato, sem uma forte antecipação de se isso será necessário com frequência no futuro, eu procuraria evitar considerar isso como uma substituição de tipo de A para B, se possível, e apenas procurá-lo como uma maneira de transformar o estado de A. Por exemplo, defina alguns campos em A para transformá-lo e aparecer como B para o usuário sem realmente mudar para um 'tipo' diferente de coisa - você pode potencialmente tornar A loja B em particular usando a composição e chamar funções em B quando o estado de A está definido para indicar que ele deve imitar B para simplificar a implementação, se possível. Essa deve ser uma solução muito simples e minimamente invasiva.

De qualquer forma, ecoando muitas outras, sugiro errar ao evitar soluções genéricas nesse caso, mas mais ainda porque presumo que isso esteja considerando a possibilidade de adicionar uma capacidade muito ousada ao sistema central, e aí eu sugiro errar ao deixar de fora, especialmente por enquanto.


fonte
"você sente falta de todos os erros que não codifica." (do cartaz de basquete: você perca todos os tiros que você não tome)
3

É difícil dar uma resposta genérica para esse problema específico ;-)

Quanto mais genérico, mais tempo você ganhará para futuras alterações. Por exemplo, por esse motivo, muitos programas de jogos usam o padrão de componente de entidade em vez de criar um sistema de tipos muito elaborado, porém rígido, dos personagens e objetos no jogo.

Por outro lado, tornar algo genérico requer um investimento inicial de tempo e esforço em design, que é muito maior do que em algo muito específico. Isso corre o risco de excesso de engenharia e até de se perder em possíveis requisitos futuros.

Sempre vale a pena ver se existe uma generalização natural que o levará a um avanço. No entanto, no final, é uma questão de equilíbrio, entre o esforço que você pode gastar agora e o esforço que pode precisar no futuro.

Christophe
fonte
3
  1. Híbrido. Isso não precisa ser uma questão de um ou outro. Você pode criar a API para conversões de tipo geral e implementar apenas a conversão específica necessária no momento. Certifique-se de que, se alguém chamar sua API geral com uma conversão não suportada, ela falhará com um status de erro "não suportado".)

  2. Testando. Para a conversão A-> B, vou ter que escrever um (ou um pequeno número) de testes. Para uma conversão genérica de x-> y, talvez seja necessário escrever uma matriz inteira de testes. Isso é substancialmente mais trabalhoso, mesmo que todas as conversões compartilhem uma única implementação genérica.

    Se, por outro lado, houver uma maneira genérica de testar todas as conversões possíveis, não haverá muito mais trabalho e talvez eu esteja inclinado a ir para uma solução genérica mais cedo.

  3. Acoplamento. Um conversor de A para B pode necessariamente precisar conhecer detalhes de implementação sobre A e B (acoplamento rígido). Se A e B ainda estão evoluindo, isso significa que talvez eu precise revisitar novamente o conversor (e seus testes), o que é uma merda, mas pelo menos é limitado a A e B.

    Se eu tivesse optado por uma solução genérica que precisa acessar os detalhes de todos os tipos, mesmo que os C e D evoluam, talvez seja necessário ajustar o conversor genérico (e vários testes), o que pode me atrasar até embora ninguém ainda precisa converter para um C ou D .

    Se as conversões genérica e específica puderem ser implementadas de uma maneira que seja apenas fracamente acoplada aos detalhes dos tipos, não me preocuparei com isso. Se um deles pode ser feito de uma maneira pouco acoplada, mas o outro exige um acoplamento rígido, então esse é um argumento forte para a abordagem de pouco acoplamento logo de cara.

Adrian McCarthy
fonte
2
Testar é um bom ponto. É uma grande vantagem se você projetar soluções genéricas com base em conceitos da teoria das categorias , porque, em geral, você obtém teoremas gratuitos que provam que a única implementação possível para uma assinatura (que o verificador de tipos do compilador aceita) é a correta , ou pelo menos que, se o algoritmo funcionar para qualquer tipo específico, também deverá funcionar para todos os outros tipos.
leftaroundabout
Eu acho que existe um motivo específico do domínio pelo qual apenas uma conversão de tipo foi solicitada, o que significa que a maioria das conversões de tipo seriam ações inválidas no domínio do problema e, por esse motivo, não deve ser suportada pelo aplicativo até que elas são oficialmente permitidos. Essa resposta se aproxima mais dessa argumentação.
Ralf Kleberhoff
3

A solução deve ser o mais genérica possível ou o mais específica possível?

Essa não é uma pergunta respondível.

O melhor que você pode obter razoavelmente é algumas heurísticas para decidir o quão geral ou específico será a criação de uma determinada solução. Trabalhando com algo como o processo abaixo, geralmente a aproximação de primeira ordem está correta (ou boa o suficiente). Quando não é, é provável que o motivo seja muito específico do domínio para ser útil em detalhes aqui.

  1. Aproximação de primeira ordem: a regra usual de três YAGNI, conforme descrito por Daniel Pryden, Doc Brown, et al .

    Essa é uma heurística geralmente útil, porque provavelmente é o melhor que você pode fazer, que não depende do domínio e de outras variáveis.

    Portanto, a presunção inicial é: fazemos a coisa mais específica.

  2. Aproximação de segunda ordem: com base no seu conhecimento especializado do domínio da solução , você diz

    A solução "genérica", neste caso, requer menos trabalho que a solução "específica"

    para que possamos interpretar novamente o YAGNI como recomendação para evitar trabalho desnecessário , em vez de evitar generalidade desnecessária. Portanto, podemos modificar nossa presunção inicial e, em vez disso, fazer a coisa mais fácil .

    No entanto, se o conhecimento do domínio da solução indicar que é provável que a solução mais fácil abra muitos bugs, seja difícil testar adequadamente ou cause qualquer outro problema, ser mais fácil de codificar não é necessariamente um motivo suficientemente bom para alterar nossa escolha original.

  3. Aproximação de terceira ordem: seu conhecimento do domínio do problema sugere que a solução mais fácil é realmente correta ou você está permitindo que muitas transições que você sabe sejam sem sentido ou erradas?

    Se a solução fácil, mas genérica, parecer problemática ou se você não estiver confiante em sua capacidade de julgar esses riscos, fazer o trabalho extra e permanecer com seu palpite inicial provavelmente será melhor.

  4. Aproximação de quarta ordem: seu conhecimento do comportamento do cliente, ou como esse recurso se relaciona com outros, ou prioridades de gerenciamento de projetos ou ... quaisquer outras considerações não estritamente técnicas modificam sua decisão de trabalho atual?

Sem utilidade
fonte
2

Esta não é uma pergunta fácil de responder com uma resposta simples. Muitas respostas deram heurísticas baseadas em uma regra de 3, ou algo semelhante. Ir além dessas regras é difícil.

Para realmente realmente responder a sua pergunta, você tem que considerar que o seu trabalho é mais provável não para implementar algo que muda A-> B. Se você é um contratado, talvez seja esse o requisito, mas se você é um funcionário, foi contratado para executar muitas tarefas menores para a empresa. Alterar A-> B é apenas uma dessas tarefas. Sua empresa vai se preocupar com o quão bem as futuras alterações podem ser feitas, mesmo que isso não esteja indicado na solicitação. Para encontrar "TheBestImplementation (tm)", você deve observar a imagem ampliada do que realmente está sendo solicitado a fazer e, em seguida, usar isso para interpretar a pequena solicitação que você recebeu para alterar A-> B.

Se você é um programador de nível baixo recém-saído da faculdade, geralmente é aconselhável fazer exatamente o que foi solicitado. Se você foi contratado como arquiteto de software com 15 anos de experiência, normalmente é aconselhável pensar em aspectos gerais. Todo trabalho real vai ficar em algum lugar entre "faça exatamente o que a tarefa restrita é" e "pense no cenário geral". Você descobrirá onde seu trabalho se encaixa nesse espectro se conversar com as pessoas o suficiente e trabalhar o suficiente para elas.

Posso dar alguns exemplos concretos em que sua pergunta tem uma resposta definitiva com base no contexto. Considere o caso em que você está escrevendo um software crítico para a segurança. Isso significa que você tem uma equipe de teste para garantir que o produto funcione conforme o prometido. Algumas dessas equipes de teste são obrigadas a testar todos os caminhos possíveis através do código. Se você conversar com eles, poderá descobrir que, se generalizar o comportamento, adicionará US $ 30.000 aos custos de teste porque eles terão que testar todos esses caminhos extras. Nesse caso, não adicione funcionalidade generalizada, mesmo se você precisar duplicar o trabalho 7 ou 8 vezes por causa disso. Economize o dinheiro da empresa e faça exatamente o que a solicitação declarou.

Por outro lado, considere que você está criando uma API para permitir que os clientes acessem dados em um programa de banco de dados criado por sua empresa. Um cliente solicita a permissão de alterações A-> B. As APIs geralmente têm um aspecto de algemas douradas: depois de adicionar funcionalidade a uma API, você normalmente não deve remover essa funcionalidade (até o próximo número da versão principal). Muitos de seus clientes podem não estar dispostos a pagar pelo custo da atualização para o próximo número da versão principal; portanto, você pode ficar com a solução que escolher por um longo tempo. Neste caso, eu altamente recomendo criar a solução genérica desde o início. Você realmente não deseja desenvolver uma API ruim cheia de comportamentos pontuais.

Cort Ammon
fonte
1

Hmm ... não há muito contexto para obter uma resposta ... ecoando respostas anteriores: "Depende".

De certa forma, você precisa recorrer à sua experiência. Caso contrário, alguém mais experiente no domínio. Você pode discutir sobre o estado dos critérios de aceitação. Se for algo parecido com 'o usuário deve poder alterar o tipo de "A" para "B"' versus 'o usuário deve ser capaz de alterar o tipo do seu valor atual para qualquer valor alternativo permitido'.

Os critérios de aceitação frequente estão sujeitos a interpretação, mas uma boa equipe de controle de qualidade pode escrever critérios apropriados para a tarefa em questão, minimizando a interpretação necessária.

Existem restrições de domínio que não permitem alterar de "A" para "C" ou qualquer outra opção, mas apenas "A" para "B"? Ou isso é apenas um requisito estritamente especificado que não é "visão de futuro"?

Se o caso geral fosse mais difícil, eu perguntaria antes de iniciar o trabalho, mas no seu caso, se eu pudesse 'prever' que outros pedidos de alteração 'tipo' viriam no futuro, ficaria tentado a: a) escreva algo reutilizável para o caso geral eb) envolva-o em uma condição que permita apenas A -> B por enquanto.

Fácil o suficiente para verificar o caso atual em testes automatizados e fácil o suficiente para abrir outras opções posteriormente, se / quando diferentes casos de uso surgirem.

railsdog
fonte
1

Para mim, uma diretriz que estabeleci há pouco é: "Para requisitos hipotéticos, apenas escreva código hipotético". Ou seja - se você antecipar requisitos adicionais, pense um pouco sobre como implementá-los e estruture seu código atual para que não bloqueie dessa maneira.

Mas não escreva código real para eles agora - pense um pouco sobre o que você faria. Caso contrário, você geralmente tornará as coisas desnecessariamente complexas e provavelmente será incomodado mais tarde quando surgirem requisitos reais diferentes do que você antecipou.

Quatro do seu exemplo: se você tiver todos os usos do método convert sob seu controle, basta chamá-lo convertAToB por enquanto e planejar usar uma refatoração de "método de renomeação" no IDE para renomeá-lo se precisar de funcionalidades mais gerais posteriormente. No entanto, se o método de conversão fizer parte de uma API pública, isso pode ser bem diferente: sendo esse específico bloquearia a generalização posteriormente, pois é difícil renomear as coisas nesse caso.

Hans-Peter Störr
fonte
0

Eu continuo ouvindo que um bom desenvolvedor deve tentar antecipar a mudança e projetar o sistema para que seja fácil estender no futuro,

Em princípio sim. Mas isso não leva necessariamente a soluções genéricas.

Existem dois tipos de tópicos no desenvolvimento de software, no que me diz respeito, nos quais você deve antecipar mudanças futuras:

  • Bibliotecas destinadas a serem usadas por terceiros e
  • arquitetura geral de software.

O primeiro caso é resolvido observando sua coesão / acoplamento, injeção de dependência ou o que for. O segundo caso está em um nível mais abstrato, por exemplo, escolhendo uma arquitetura orientada a serviços em vez de um grande blob de código monolótico para um aplicativo grande.

No seu caso, você está solicitando uma solução específica para um problema específico, que não tem impacto previsível no futuro. Nesse caso, YAGNI e DRY são bons lemas para serem usados:

  • YAGNI (você não vai precisar disso) diz para você implementar o material absolutamente absolutamente básico que você precisa e usa agora . Isso significa implementar o mínimo que faz com que seu conjunto de testes atual fique de vermelho para verde, se você usar o desenvolvimento no estilo TDD / BDD / FDD. Nem uma única linha a mais.
  • DRY (não repetir-se) significa que se você vir sobre um problema semelhante novamente, então você dê uma boa olhada em se você precisa de uma solução genérica.

Combinado com outras práticas modernas (como uma boa cobertura de teste para poder refatorar com segurança), isso significa que você acaba com um código médio rapidamente escrito, enxuto, que cresce conforme necessário.

qual parece ser uma solução genérica?

Não, parece que você deve ter ambientes de programação, linguagens e ferramentas que tornem fácil e divertido refatorar quando necessário. Soluções genéricas não fornecem isso; eles separam o aplicativo do domínio real.

Dê uma olhada nas estruturas modernas de ORMs ou MVC, por exemplo, Ruby on Rails; no nível do aplicativo, todo o foco está em realizar trabalhos não genéricos. As bibliotecas Rails em si são obviamente quase 100% genéricas, mas o código de domínio (sobre o qual sua pergunta se refere) deve estar fazendo o mínimo de travessuras nesse aspecto.

AnoE
fonte
0

Uma maneira diferente de pensar sobre o problema é considerar o que faz sentido.

Por exemplo, havia um aplicativo que eu estava desenvolvendo que tinha seções que faziam quase a mesma coisa, mas tinham regras de permissão inconsistentes. Como não havia motivo para mantê-los diferentes, quando refatorei essa seção, obriguei todos a fazer permissões da mesma maneira. Isso tornou o código geral menor, mais simples e a interface era mais consistente.

Quando a gerência decidiu permitir que outras pessoas acessassem um recurso, conseguimos fazê-lo apenas alterando uma sinalização.

Obviamente, faz sentido fazer a conversão de tipo específico. Também faz sentido fazer conversões de tipo adicionais?

Lembre-se de que, se a solução geral for mais rápida de implementar, o caso específico também será fácil, basta verificar se é a única conversão de tipo que você está permitindo.

Se o aplicativo estiver em uma área altamente regulamentada (um aplicativo médico ou financeiro), tente envolver mais pessoas em seu projeto.

Robert Baron
fonte