Remoção de valores codificados e design defensivo versus YAGNI

10

Primeiro um pouco de fundo. Estou codificando uma pesquisa de Idade -> Taxa. Existem 7 faixas etárias, portanto, a tabela de pesquisa é composta por 3 colunas (de | a | taxa) com 7 linhas. Os valores raramente mudam - são taxas legisladas (primeira e terceira colunas) que permaneceram as mesmas por três anos. Achei que a maneira mais fácil de armazenar esta tabela sem codificá-la é no banco de dados em uma tabela de configuração global, como um valor de texto único contendo um CSV (então "65,69,0.05,70,74,0.06" é como as camadas 65-69 e 70-74 seriam armazenadas). Relativamente fácil de analisar e usar.

Então percebi que, para implementar isso, eu teria que criar uma nova tabela, um repositório para envolvê-la, testes de camada de dados para o repositório, testes de unidade em torno do código que descompacta o CSV na tabela e testes em torno da própria pesquisa. O único benefício de todo esse trabalho é evitar a codificação da tabela de pesquisa.

Ao conversar com os usuários (que atualmente usam a tabela de pesquisa diretamente - olhando para uma cópia impressa), a opinião é praticamente de que "as taxas nunca mudam". Obviamente, isso não está correto - as taxas foram criadas apenas três anos atrás e, no passado, coisas que "nunca mudam" têm o hábito de mudar - então, para eu programar isso de forma defensiva, eu definitivamente não devo armazenar a tabela de consulta em a aplicação.

Exceto quando penso YAGNI . O recurso que estou implementando não especifica que as taxas serão alteradas. Se as taxas mudarem, elas ainda mudarão tão raramente que a manutenção nem sequer é considerada, e o recurso não é realmente crítico o suficiente para que qualquer coisa seja afetada se houver um atraso entre a alteração da taxa e o aplicativo atualizado.

Decidi praticamente que nada de valor será perdido se eu codificar a pesquisa e não estiver muito preocupado com minha abordagem desse recurso em particular. Minha pergunta é: como profissional, justifiquei adequadamente essa decisão? A codificação dos valores é um design ruim, mas o problema de remover os valores do aplicativo parece violar o princípio YAGNI.

EDIT Para esclarecer a questão, não estou preocupado com a implementação real. Preocupa-me que eu possa fazer algo rápido e ruim e justificá-lo dizendo YAGNI, ou posso adotar uma abordagem mais defensiva e de alto esforço, que, mesmo na melhor das hipóteses, traz benefícios baixos. Como programador profissional, minha decisão de implementar um projeto que eu sei que é falha simplesmente se resume a uma análise de custo / benefício?

EDIT Embora todas as respostas tenham sido muito interessantes, pois acho que isso se resume às escolhas de design de um indivíduo, acho que as melhores respostas foram @ Corbin e @EZ Hart, pois trazem à tona coisas que eu não havia considerado na pergunta:

  • a falsa dicotomia de 'remover corretamente valores codificados' movendo-os para o banco de dados vs 'aplicando YAGNI' de forma eficiente 'usando codificação embutida. Havia uma terceira opção de colocar a tabela de pesquisa na configuração do aplicativo, o que não implica a sobrecarga da maneira correta e sem a eficiência do YAGNI. Geralmente, não estamos limitados a decisões ou decisões e, em seguida, tudo se resume a uma decisão de custo / benefício.
  • a geração de código pode reduzir a sobrecarga de mover os valores codificados para o banco de dados e de uma maneira que também elimina minha decisão de engenharia de processar um CSV na tabela. Potencialmente, isso também adiciona um problema de manutenção a longo prazo ao código gerado, se os requisitos básicos mudarem para o método de pesquisa. Tudo isso afeta apenas a análise de custo / benefício, e é provável que, se eu tivesse essa automação disponível, nem sequer consideraria a codificação embutida algo assim.

Estou marcando a resposta de @ Corbin como correta, pois altera minhas suposições de custo de desenvolvimento e provavelmente adicionarei algumas ferramentas de geração de código ao meu arsenal no futuro próximo.

Rebecca Scott
fonte
Não se esqueça, se as taxas mudarem e tudo o que você tiver forem valores codificados, você poderá atrapalhar os cálculos de registros históricos quando as taxas mudarem (e serão, independentemente do que o seu cliente está lhe dizendo).
219 Andy

Respostas:

6

Você encontrou uma falha no seu processo de desenvolvimento. Quando é difícil fazer a coisa certa (criar a tabela, repo, repo testes, nivelar testes ...), os desenvolvedores encontrarão uma maneira de contornar isso. Isso geralmente envolve fazer a coisa errada. Nesse caso, é tentador tratar os dados do aplicativo como lógica do aplicativo. Não faça isso. Em vez disso, adicione automações úteis ao seu processo de desenvolvimento. Usamos o CodeSmith para gerar o código chato e padronizado que ninguém deseja escrever. Depois de criar uma tabela, executamos o CodeSmith e ele gera os DAOs, DTOs e elimina os testes de unidade para cada um.

Dependendo das tecnologias que você está usando, você deve ter opções semelhantes. Muitas ferramentas ORM geram modelos a partir de um esquema existente. As migrações de trilhos funcionam na direção oposta - tabelas de modelos. A metaprogramação em linguagens dinâmicas é particularmente forte na eliminação do código padrão. Você precisará trabalhar um pouco mais para gerar tudo o que precisa se tiver um aplicativo complexo de várias camadas, mas vale a pena. Não deixe que o sentimento "uau, isso é uma dor no pescoço" o impeça de fazer a coisa certa.

Ah, e não armazene seus dados em um formato que exija processamento adicional (CSV). Isso apenas adiciona etapas extras que requerem sua atenção e testes.

Março de Corbin
fonte
Eu não diria que uma migração do Rails cria tabelas a partir de modelos ... As migrações do Rails descrevem alterações nas tabelas. A execução de uma migração altera o banco de dados. As propriedades do modelo que correspondem à estrutura da tabela são criadas em tempo de execução.
kevin Cline
Isso me interessa, eu não tinha pensado em automatizar partes do esforço inicial para esse nível. E ter essa automação significaria que eu poderia configurar uma tabela completa em vez de usar o CSV para economizar tempo. O que me preocupa é a manutenção a longo prazo do código gerado. Ao gerá-lo antecipadamente, assumi antecipadamente que o método de fazer a pesquisa nunca mudaria. Eu acho que se for uma possibilidade, eu deveria levar isso em consideração como parte do custo / benefício, fornecendo o custo reduzido do padrão. 1
Rebecca Scott
A pergunta era: "devo codificar valores quando meu cliente diz que não vai mudar?" e sua resposta é "não, e de fato você deve lançar um ORM no problema". Discordo. Existem opções mais simples que permitiriam ao OP evitar a codificação. Fazer grandes acréscimos à arquitetura para suportar coisas explicitamente designadas como desnecessárias não é ético. É lamentável que muitos desenvolvedores estejam predispostos a fazer essas coisas. E devo dizer que discordo 100% da sua sugestão de não "deixar o sentimento 'uau, isso é uma dor no pescoço' impedi-lo de fazer a coisa certa". Esses sentimentos importam!
user1172763
10

Para encerrar e estender a resposta de Thorbjørn Ravn Andersen: Manter o cálculo / pesquisa em um só lugar é um bom começo.

Seu processo de pensamento defensivo contra YAGNI é um problema comum. Nesse caso, sugiro que seja informado por mais duas coisas.

Em primeiro lugar - como foi apresentado o requisito do usuário? Eles especificaram a capacidade de edição das taxas? Caso contrário, a complexidade adicionada faz parte de algo que você pode cobrar ou não? (ou se você faz parte da equipe, pode dedicar seu tempo a outros trabalhos?) Em caso afirmativo, vá em frente e entregue o que foi razoavelmente solicitado.

Em segundo lugar, e talvez mais importante, é improvável que a mera editabilidade cumpra um requisito real diante de uma mudança legislada. Se e quando as taxas mudarem, é provável que haja uma data de transição e algum processamento antes e depois que tenha que acontecer. Além disso, se essa mesma interface precisar executar qualquer tipo de processamento de reivindicações com data anterior, a taxa correta poderá ter que ser pesquisada com base na data efetiva da entrada, e não na data real.

Em resumo, estou observando que um requisito editável real pode muito bem ser bastante complexo, portanto, a menos ou até que seja desenvolvido, o simples é provavelmente melhor.

Boa sorte

sdg
fonte
11
+1 para o seu segundo ponto. Não tento questionar o que os órgãos legislativos podem fazer no futuro.
21411 David Thornley
Faça isso em uma função de biblioteca. Não há problema em ter apenas um usuário. Quando e se os requisitos mudam, você tem um lugar para mudar. (Você pode precisar adicionar uma função para fazer pesquisas para o valor a partir de uma determinada data.)
BillThor
Melhor e mais completa resposta, +1
Andy
7

Tornar a pesquisa real uma função de biblioteca. Você pode ver todo o código usando essa pesquisa no seu repositório de origem, para saber quais programas precisam ser atualizados quando as taxas mudam.


fonte
A pesquisa será implementada apenas em um único local (algo como uma PensionRateLookupclasse, talvez) que será usada globalmente. Eu faria isso independentemente de ser armazenado fora do aplicativo ou codificado permanentemente, dessa forma, apenas a implementação da PensionRateLookupclasse precisa ser mantida. Meu problema é como eu usei o YAGNI para concluir que a codificação embutida da tabela de pesquisa é aceitável.
Rebecca Scott
Obrigado pela sua resposta embora. Manter a implementação da pesquisa em um só lugar é definitivamente um bom design.
Rebecca Scott
O YAGNI é válido apenas se você puder enviar novos binários quando as taxas mudarem. Se você não pode, mas pode alterar a configuração, faça com que seja lido como um valor de configuração na inicialização com a representação textual atual como padrão.
Eu envio uma nova versão geralmente semanalmente, portanto, atualizar a pesquisa não é um problema. Colocá-lo na configuração do cliente é realmente pior, pois não consigo alterar a configuração do cliente com um novo binário. A alternativa que eu vejo é colocá-lo no banco de dados, o que significa muito esforço.
Rebecca Scott
então, qual é o problema? Quando as taxas mudam, você atualiza o código e envia para todos os clientes?
2

Deixe-me ver se entendi sua pergunta. Você tem duas opções para implementar um recurso: ou codifica um valor e seu recurso é fácil de implementar (mas você não gosta da parte do código rígido) ou faz um grande esforço para "refazer" muitas coisas que são feitas para que você possa desenvolver seus recursos de maneira limpa. Isso está correto?

A primeira coisa que me vem à cabeça é "A coisa mais importante sobre conhecer as boas práticas é saber quando você está melhor sem elas".

Nesse caso, o esforço é muito alto para que você possa fazer isso de maneira limpa, as chances de efeito colateral dessa mudança são grandes e o retorno que você obterá é pequeno (como você explicou, parece provável que não mudança).

Eu usaria a abordagem de código rígido (mas a prepararia para ser flexível no futuro) e, em caso de alteração dessa taxa no futuro, aproveitaria a oportunidade para refatorar toda essa seção de design incorreto do código. Portanto, o tempo e o custo podem ser estimados corretamente e o custo da alteração do valor codificado seria mínimo.

Esta seria a minha abordagem :)

JSBach
fonte
Obrigado @Oscar. A parte técnica disso é def. corrigir. E eu concordo com a forma como você abordaria o problema, refatorando apenas quando necessário. Então, você está dizendo que, como profissional, podemos e devemos escolher nossos princípios de design, com base unicamente em custo / benefício? Faz sentido.
Rebecca Scott
@ Ben Scott: De nada :). Sim, no meu ponto de vista, os princípios de design foram criados / catalogados não porque parecem bonitos, mas porque trazem benefícios como código "limpeza", flexibilidade, robustez, etc. Mas sempre há duas perguntas: 1- Preciso disso ? 2 - Minhas restrições (tempo, técnico, etc) me permitem implementá-lo? Isso é geralmente o que me leva a escolher meu princípio de design. O excesso de engenharia também é ruim;) ps: se você acha que é uma resposta interessante, vote nele para que outras pessoas também o leiam com uma probabilidade mais alta. Obrigado! :)
JSBach
upvoted ;-)
Rebecca Scott
2

Este é um item que "não mudará" até que isso aconteça. É inevitável que isso mude, mas esse tempo pode estar um pouco distante.

Sua justificativa está correta. No momento, o cliente não solicitou a capacidade de alterar facilmente essas taxas. Como tal, YAGNI.

No entanto, o que você não quer é o código que acessa suas taxas e interpreta os resultados espalhados por toda a base de código. Um bom design de OO faria você encapsular a representação interna de suas taxas em uma classe e expor apenas os um ou dois métodos necessários para usar os dados.

Você precisará desse encapsulamento para evitar erros de copiar e colar, ou ter que executar uma refatoração em todo o código que usa as taxas quando precisar fazer uma alteração na representação interna. Quando você toma essa precaução inicial, a abordagem mais complicada pode ser uma simples troca e substituição para a versão mais destacada.

Além disso, chame a limitação do design atual para o cliente. Diga a eles que, no interesse de manter a programação, você codificou os valores - o que requer uma alteração na codificação para atualizá-los. Dessa forma, ao tomar conhecimento das alterações de taxa pendentes devido à nova legislação, eles podem optar por simplesmente atualizar a tabela de pesquisa ou executar as alterações mais complicadas naquele momento. Mas coloque essa decisão no colo deles.

Berin Loritsch
fonte
Obrigado @berin, não mencionei o SRP na pergunta, mas estava no plano. Bom ponto é devolver a propriedade do problema ao cliente.
Rebecca Scott
Atribuir a culpa por quando as coisas dão errado no futuro não me parece profissional.
219 Andy
@ Andy, que parte disso está atribuindo culpa? Apresentar as limitações de design ao cliente permite a eles priorizar o trabalho complicado agora e tirar outras coisas da mesa, atrasar o prazo ou aceitar o design limitado agora, pois outras coisas na mesa são mais importantes para eles. Você está capacitando seu cliente com a escolha do produto. Informar o cliente do risco / recompensa das escolhas que você deseja fazer no interesse deles tornará o projeto mais suave.
Berin Loritsch
@BerinLoritsch Mas essa decisão específica de design é semelhante ao uso de peças abaixo do padrão, dizendo "Posso usar uma massa de encanador para conectar esse tubo com vazamento, essa é a sua opção mais barata!" As taxas mudarão. É um dado, e é irresponsável para um profissional construir um sistema que não o permita. E a economia provavelmente é insignificante no grande esquema do custo do projeto. Coloque os dados em uma tabela; o código que obtém os dados da pesquisa é um pouco diferente, a lógica que determina qual taxa usar é a mesma. A única outra decisão é como lidar com dados históricos depois do ...
Andy
alterações nas taxas, que geralmente são tratadas apenas copiando a taxa no registro relevante. Nenhuma tela de administrador é necessária neste momento, o sistema poderá lidar com a alteração das taxas e será um script simples para alterá-las. Nada disso deve demorar mais do que algumas horas, mas impedirá que o porão seja inundado no próximo ano, quando a massa falhar.
219 Andy
2

Divida a diferença e coloque os dados da taxa em uma configuração. Você pode usar o formato CSV que você já possui, evitar a sobrecarga desnecessária do banco de dados e, se a alteração for necessária, será uma alteração que o cliente poderá fazer sem precisar recompilar / reinstalar e sem mexer no banco de dados - eles podem apenas editar o arquivo de configuração.

Geralmente, quando você olha para uma escolha entre dois extremos (viola o YAGNI x os dados dinâmicos codificados), a melhor resposta está em algum lugar no meio. Cuidado com falsas dicotomias.

Isso assume que toda a sua configuração está em arquivos; se for um lugar difícil como o registro, você provavelmente deve desconsiderar este conselho :)

EZ Hart
fonte
Eu considerei tê-lo em uma configuração, mas deixei de lado porque os usuários não são muito técnicos na edição de uma sequência CSV; portanto, seria eu quem atualizaria a configuração para N usuários (como faço todo o suporte de TI em também), em termos de esforço, seria mais fácil fazer o trabalho inicial para inseri-lo no banco de dados que eu posso administrar de um só lugar. Suponho que se isso não fosse uma consideração (se os usuários fossem capazes de gerenciar sua configuração individual) eu não teria esse problema. Então, um ponto muito bom, obrigado.
Rebecca Scott
Se você combinou isso com outras sugestões da lógica centralizada, é uma ótima resposta. É pura maldade realmente pesar na pesquisa, em vez de colocar de uma maneira que permita que ela seja alterada via config. Sempre que qualquer outro desenvolvedor se deparar com ele, ele o odiará. Mesmo uma loja de desenvolvimento individual deve considerar o que acontece se for atropelada por um ônibus e facilitar o próximo desenvolvedor a alterar os valores sem uma versão completa do software. Somente se você odeia o cliente e todos os outros desenvolvedores, deve criticá-lo. Então, basicamente, nunca.
simbo1905
2

Achei que a maneira mais fácil de armazenar esta tabela sem codificá-la é no banco de dados em uma tabela de configuração global, como um valor de texto único contendo um CSV (então "65,69,0.05,70,74,0.06" é como as camadas 65-69 e 70-74 seriam armazenadas.

Acabei de criar uma tabela de banco de dados. (Você estava pensando em armazenar uma mesa inteira em um arquivo, ficou louco?)

A tabela precisa de 2 campos, NÃO de 3. Idade e taxa. Esta próxima linha contém o valor superior! Você está desnormalizando sem nem mesmo saber!

Aqui está o Sql para obter a taxa para alguém com mais de 67 anos.

Select * from RateTable where Age in (Select max(age) from RateTable where age <=67) 

Não se preocupe em fazer uma tela de manutenção, pois ela está fora de escopo. Se eles solicitarem mais tarde, emita uma solicitação de alteração e faça-a.

EDIT: Como outros disseram manter o código, a taxa fica centralizada, caso toda a estrutura da taxa seja alterada.

Idiotas
fonte
Olá @Morons, obrigado pela sua resposta. A tabela precisaria, na verdade, de 3 colunas. É uma taxa única por faixa etária, idade mínima a máxima (65 a 69 anos de idade têm 5%). E eu estou armazenando apenas uma pequena quantidade de dados para uma finalidade limitada, então o que há de errado em fazer uma suposição sobre a estrutura e optar por um CSV em vez de uma tabela dedicada inteira? Eu provavelmente adicionaria um delimitador de linha, e dividiria por linha e coluna e puxaria as colunas para os campos obrigatórios. Isso sempre será lido muito mais do que escrito, por isso não estou muito preocupado com uma tabela completa.
Rebecca Scott
A próxima linha contém o valor superior do intervalo ... Isso é duplicação de dados. Dizer <69 e> 7 é a mesma coisa. O único motivo para ter os dois valores é se o intervalo pode ter furos. ... Estou dizendo para usar uma tabela porque isso é mais fácil (e tem um design melhor). Eu não entendo por que você acha que armazenar um csv em uma tabela poupará tempo ou esforço. Você ainda precisa de uma chamada de banco de dados apenas para obter essa sequência, e precisa analisá-la. Como mostrei acima, você pode obter a tarifa com uma única chamada de banco de dados.
21911 idiotas
Ah verdade. Eu estava mantendo a mesa em si semelhante ao material de origem ... e perdi que a idade é o limite superior na sua resposta, porque eu tinha as tristezas que lhe dei.
Rebecca Scott
11
"Você está desmoralizando sem saber!" - no começo eu achava que isso era um erro de digitação e que significava 'desnormalizar', mas quanto mais eu pensava nisso, mais parecia que você pode estar certo de qualquer maneira :)
EZ Hart
@ez hart LOL verdade.
Rebecca Scott
1

Eu concordo com a maioria das respostas dadas. Eu também acrescentaria que a consistência com o restante do aplicativo é importante. Se este é o único local no código que possui valores codificados, provavelmente surpreenderá os mantenedores. Isto é especialmente verdade se for uma base de código grande e estável. Se é um pequeno programa mantido por você, a decisão é menos importante.

Tenho uma memória distante de ler um conhecido cara ágil / OOP (como Dave Thomas ou Kent Beck ou alguém) dizendo que a regra geral para duplicação de código era duas e apenas duas: não refatorar a primeira vez que você duplicar algo desde você pode escrever apenas duas vezes na sua vida. Mas a terceira vez ...

Isso não trata exatamente da questão, já que você está questionando a YAGNI, mas acho que ela fala da flexibilidade geral das regras ágeis. O ponto do ágil é se adaptar à situação e seguir em frente.

Dave
fonte
Obrigado @Dave, isso é útil. Sou o único mantenedor, desde o início, mas é uma base de código grande e relativamente estável (milhares de arquivos, mais de 100 tabelas, etc.) e ainda me surpreendo constantemente (e consternado). A consistência é definitivamente um dos meus objetivos daqui para frente.
Rebecca Scott
0

Código rígido em uma função.

  • Quando os valores mudam, você pode cobrar o cliente novamente
  • Quando os valores mudam, é provável que o formato da tabela precise ser alterado, fazer CSV perderá tempo
  • Mais fácil de implementar, maiores chances de estar dentro do orçamento com o contrato atual
  • Pode encontrar facilmente quando precisará ser atualizado
earlNameless
fonte
Deixe-me instalar esse valor abaixo do padrão; portanto, quando ele certamente quebra em um ano, eu recebo negócios repetidos. Isso parece sombrio, porque é.
219 Andy