Um servidor deve ser "branda" no que aceita e "descartar entradas defeituosas silenciosamente"?

27

Fiquei com a impressão de que agora todos concordam que essa máxima foi um erro. Mas eu vi recentemente essa resposta, que tem um comentário "seja branda", votada 137 vezes (até hoje).

Na minha opinião, a clemência no que os navegadores aceitam foi a causa direta da bagunça total que o HTML e alguns outros padrões da web eram há alguns anos atrás, e só recentemente começaram a se cristalizar adequadamente dessa bagunça. A forma como eu vejo, sendo branda no que você aceitar irá levar a isso.

A segunda parte da máxima é "descartar entrada defeituosa silenciosamente, sem retornar uma mensagem de erro, a menos que isso seja exigido pela especificação" , e isso parecerá ofensivo. Qualquer programador que bater a cabeça na parede quando algo falhar silenciosamente saberá o que quero dizer.

Então, eu estou completamente errado sobre isso? Meu programa deve ser tolerante com o que aceita e engolir erros silenciosamente? Ou estou interpretando mal o que isso significa?


A pergunta original dizia "programa", e eu entendo a opinião de todos sobre isso. Pode fazer sentido que os programas sejam brandos. O que eu realmente quis dizer, no entanto, são APIs: interfaces expostas a outros programas , e não a pessoas. HTTP é um exemplo. O protocolo é uma interface que somente outros programas usam. As pessoas nunca fornecem diretamente as datas que entram nos cabeçalhos como "If-Modified-Since".

Portanto, a pergunta é: o servidor que implementa um padrão deve ser branda e permitir datas em vários outros formatos, além do que é realmente exigido pelo padrão? Eu acredito que o "ser tolerante" deve se aplicar a essa situação, ao invés de interfaces humanas.

Se o servidor for indulgente, pode parecer uma melhoria geral, mas acho que na prática só leva a implementações de clientes que acabam confiando na indulgência e, portanto, deixam de trabalhar com outro servidor indiferente de maneiras ligeiramente diferentes.

Portanto, um servidor que expõe alguma API é branda ou é uma péssima ideia?


Agora, no tratamento branda da entrada do usuário. Considere o YouTrack (um software de rastreamento de bugs). Ele usa um idioma para entrada de texto que lembra o Markdown. Exceto que é "branda". Por exemplo, escrevendo

- foo
- bar
- baz

não é uma maneira documentada de criar uma lista com marcadores e, no entanto, funcionou. Consequentemente, acabou sendo muito utilizado em todo o nosso bugtracker interno. A próxima versão é lançada e esse recurso branda começa a funcionar de maneira um pouco diferente, quebrando várias listas que (mis) usavam esse (não) recurso. A maneira documentada de criar listas com marcadores ainda funciona, é claro.

Portanto, meu software deve ser indulgente em quais entradas de usuário ele aceita?

Roman Starkov
fonte
4
Em relação a "descartar entrada defeituosa silenciosamente", perguntaria para cada caso o que deve ser considerado como entrada defeituosa. Se você fizer uma pergunta a um usuário e esperar "sim" ou "não", "SIM" é uma entrada incorreta? Que tal "y"? Que tal "oui"? Em geral, não tenha vergonha de dizer ao usuário que sua entrada não é o que você espera. No entanto, verifique se você foi o mais inclusivo possível - na minha opinião, é isso que significa "seja indulgente".
3
Se você está falando sobre a entrada do usuário final - que se relaciona com as linhas de amizade do usuário do seu aplicativo, você deve ser leinente; para entrada automatizada gerada por máquina (de uma API), você deve ser detalhado (estrito).
Burhan Khalid
2
Na verdade, a indulgência do HTML foi o motivo pelo qual ele se tornou tão popular (e o rigor do XHTML por que ele foi descartado).
12119 Oliver Weiler
1
Eu acho que a chave é que, se é um cenário em que você pode permitir que falhe normalmente, você registra o evento no mínimo.
Rig
2
@OliverWeiler Sinto que o fracasso do XHTML tem algo a ver com o fato de que era totalmente desnecessário. O HTML já estava lá e meio que funcionou. Além disso, embora o HTML seja imensamente popular, é meio triste que estamos chamando essa tecnologia de sucesso. Satisfaz a demanda, mas o faz tão bem quanto a Symbian atendeu à demanda por smartphones.
Roman Starkov

Respostas:

9

Claro que você está completamente certo. Os programas nunca devem ser "brandos", pois isso serve apenas para mascarar problemas. Os problemas devem ser destacados, não varridos para debaixo do tapete. Uma mensagem de erro informativa é uma necessidade absoluta para que um programa seja útil ao usuário.

Na maioria das vezes, quando dados incorretos / inválidos são fornecidos, o provedor desses dados (seja um usuário ou a saída de outro programa) provavelmente não sabia que era inválido. A ingestão do erro os manterá na crença de que é (ou pode ser) válido, o que prolifera dados inválidos.

A única maneira de os sistemas interoperarem é que essa interoperação seja definida total e inequivocamente. Um programa que aceita dados fora da especificação torna esses dados de fato aceitos, mesmo que sejam inválidos pela especificação, o que não apenas dificulta a compatibilidade de uma carga enorme, mas também significa que não é mais formalmente definido. O próprio programa é agora o padrão de fato . Portanto, é impossível desenvolver programas mais brandos ou substituí-los porque você não pode fazer a menor alteração na maneira como ele opera.

Timwi
fonte
25

Eu acho que tudo depende de quem é o seu público-alvo. Se são programadores, absolutamente não. Seu programa deve falhar com força e gritar assassinato sangrento. No entanto, se seu público-alvo não for programador, ele deverá ser brando, onde possa lidar com as exceções com elegância; caso contrário, sussurre um doce assassinato.

Como um estudo de caso, use o NPAPI Flash player. Existe uma versão de "lançamento" para quem realmente não se importa com 99% dos erros que podem ocorrer, mas há também uma versão de "depuração" que pode ser usada e grita assassinato sangrento quando algo dá errado. Cada suporte é obviamente reproduzido em Flash, mas tem como alvo dois dados demográficos completamente diferentes.

No final, acho que o importante é: Com o que seus usuários se preocupam?

Demian Brecht
fonte
4
A grande maioria das ferramentas de linha de comando do Unixy que afirmam ter um público-alvo fora dos programadores é, no entanto, inútil para os usuários que cometem erros. Mesmo se você não for um programador, geralmente é melhor para um programa explicar um problema do que fazer algo sem sentido ou sem intenção.
Timwi
2
@romkyns: Não completamente, estou dizendo que seu aplicativo deve lidar com erros de maneiras que façam sentido para seus usuários-alvo.
22812 Demian Brecht
@Timwi: Nesse caso, essas ferramentas de linha de comando UNIXy são mal arquitetado;)
Demian Brecht
3
@romkyns - Eu acho que um bom exemplo seria: No modo de depuração, você deseja que um programa pare com qualquer problema e diga o que deu errado. No modo de produção, você deseja que seu programa continue funcionando da melhor maneira possível e registre quaisquer problemas que ele possa resolver. Dessa forma, os programadores podem ver o que fizeram de errado e corrigi-lo, mas os usuários não se incomodarão com coisas que não podem consertar. Obviamente, alguns problemas não podem ser resolvidos, mas um bom exemplo são as regras de estilo CSS, nas quais você ainda pode renderizar um site, mesmo que não entenda uma das regras de estilo.
Reponha Monica
1
O comentário de @ BrendanLong praticamente atinge a cabeça - às vezes a produção é mais importante do que estar correta. Alguns erros (ou avisos) podem ser recuperados normalmente, sem a entrada do usuário; cabe a você decidir o que deseja que seu aplicativo faça nesses casos.
Daniel B
7

Existem dois tipos de "branda": uma é aceitar entradas incorretas e tentar entendê-las, e a outra é aceitar tipos diferentes de entradas.

Geralmente, você sempre quer o segundo quando for possível. O primeiro é quando você morre rápido e com força. Um exemplo: datas.

Aqui estão algumas entradas de exemplo, incluindo válidas, inválidas e ambíguas.

  • 2011-01-02
  • 01/02/2011
  • Jan 2, 2011
  • 2-Jan-2011
  • Green

Há apenas uma entrada inválida aqui: Green. Nem tente aceitá-lo como uma data. Desde aGreen obviamente não é uma data, é um caso em que falhas silenciosas são aceitáveis.

01/02/2011é válido, mas ambíguo. Você não sabe necessariamente se foi ou não inserida como uma data nos EUA (2 de janeiro) ou não (1 de fevereiro). Aqui, provavelmente é melhor falhar alto e pedir ao usuário uma data inequívoca.

2011-01-02geralmente é considerado inequívoco; portanto, geralmente é bom seguir em frente e assumir que é o formato "AAAA-MM-DD", e falhar apenas mais adiante. É um pouco de julgamento, no entanto, ao lidar com a entrada do usuário.

Jan 2, 2011e 2-Jan-2011são válidos e inequívocos, devem ser aceitos. No entanto, tambémThe Second of January of the year 2011 é válido e inequívoco, mas ir tão longe em prol da clemência é um exagero. Vá em frente e falhe silenciosamente, assim comoGreen .

Em suma , a resposta é "depende". Dê uma olhada no que pode ser inserido e verifique se você nunca aceita tipos de entrada conflitantes (como DD/MM/YYYYvs MM/DD/YYYY).

No contexto da pergunta / comentário vinculado , esse é um caso de 2011-01-02. A entrada se parece com JSON e será validada como JSON, mesmo que o tipo de mimet esteja errado; vá em frente e tente usá-lo mesmo se falhar em algum momento mais abaixo na linha.

Izkata
fonte
1
Há uma coisa que você não está considerando aqui. Se o usuário digitou essa sequência, sim, devo aceitar vários formatos, sem dúvida. Mas estamos falando de APIs. Os clientes das APIs são outros programas. Se for branda em seu formato de data, todo servidor futuro que expor essa API precisará ser brando exatamente da mesma maneira ou correndo o risco de incompatibilidade. A indulgência acaba sendo mais prejudicial do que útil, você não acha?
Roman Starkov
1
@romkyns Acho que você está mal entendendo onde está a indulgência. A API deve ser branda no que aceita (ele deve compreender todos 2011-01-02, Jan 2, 2011e 2-Jan-2011, se não é muito difícil de implementar), não no que ela produz . Os futuros clientes dessa API nem precisam saber sobre nenhum, desde que estejam inserindo corretamente um deles. Idealmente, a camada da API converteria tudo isso na mesma representação interna que o código usa antes de transmiti-lo.
Izkata
@romkyns A saída pode, por exemplo, sempre estar em 2011-01-02formato, e é isso que você coloca na documentação. Não vejo nenhum efeito prejudicial.
Izkata
4
@ Izkata: Você entendeu mal. Imagine que havia um programa antigo que está disponível apenas como binário. Você precisa escrever um novo programa que aceite as mesmas entradas que o antigo. Se o programa antigo foi bem definido no que ele aceita, seu trabalho está bem definido. Se for branda, seu trabalho é impossível.
Timwi
1
Discordo totalmente. a menos que seja entrada inserida pelo usuário, sempre seja rigoroso na entrada e na saída. O que acontece quando seu serviço precisa ser reimplementado? Você documentou todos os formatos de data possíveis? Você precisará implementá-los todos, pois não deseja que clientes antigos quebrem. Por favor, use a ISO 8601 para todas as instâncias e períodos de data gerados por máquina: ela é bem especificada e amplamente disponível nas bibliotecas. A propósito, o que realmente significa 02/01/2011? O período de 00:00 a 2ª até 00:00 a 3ª? Em que fuso horário?
Dibbeke
6

Falhar silenciosamente é a pior coisa que você poderia fazer. Você tentou depurar uma API com falha silenciosa? É impossível .

Há "Faça o seu melhor para se recuperar, mas envie um erro detalhado" e há "Falha silenciosa".

DeadMG
fonte
3

Parece-me que a Lei de Postel - "Seja conservador no que faz, seja liberal no que aceita dos outros" é o que está sendo discutido para um serviço JSON. Isso geralmente é aplicado a serviços da web e não à interface do usuário.

Para obter feedback construtivo do usuário da interface do usuário e restringir a entrada do usuário, é a regra geral que usamos.

MarcLawrence
fonte
Mas se você olhar as respostas aqui, todo mundo parece concordar que isso só faz sentido para as UIs, ou seja, o oposto da lei original.
Roman Starkov
Entendo o que você está dizendo e concordo com os pôsteres de que uma API / Serviço estritamente limpo é o objetivo, mas, para o bem ou para o mal, sei que adicionei 'robustez' de uma forma ou de outra aos meus serviços. Geralmente uma conversão de valor ou dois no limite. Desde que o significado seja claro, o aplicativo sabe como processar a mensagem e nenhuma regra de negócios é violada; a adição de robustez ajudará a interoperabilidade.
MarcLawrence
Até que alguém vá e implemente suas especificações, apenas para descobrir que a "robustez", na qual centenas de clientes passaram a confiar, não estava realmente nas especificações e precisa ser projetada de forma reversa ...
Roman Starkov
3

Eu acho que isso está bem coberto no capítulo 1, seção 6 da TAOUP. Especificamente, a regra de reparo , que afirma que um programa deve fazer o que pode com uma entrada, transmitir os dados corretos e, se a resposta correta falhar, faça-o o mais rápido possível.

Um conceito semelhante é a programação defensiva . Você não sabe que tipo de entrada receberá, mas seu programa deve ser robusto o suficiente para cobrir todos os casos. Isso significa que deve ser programado em casos de recuperação para problemas conhecidos, como entrada desconfigurada, e uma captura de todos os casos para lidar com incógnitas.

Portanto, descartar entradas defeituosas silenciosamente é bom, desde que você esteja manipulando essas entradas. Você nunca deve jogá-lo no chão, por assim dizer.


Para uma API, acho que ser tolerante é o mesmo que para um programa. A entrada ainda está errada , mas você está tentando reparar o máximo possível. A diferença é o que é considerado reparo válido . Como você aponta, uma API branda pode causar problemas, pois as pessoas usam "recursos" que não existem.

Obviamente, uma API é apenas uma versão de nível inferior da regra de composição . Como tal, é realmente coberto pela regra da menor surpresa , já que é uma interface.

Como observa a citação de Spencer, evite similaridades superficiais, que podem ser discutidas sobre entradas "imprecisas". Sob essas condições, eu normalmente argumentaria que tudo indica que o programa não pode pode ser reparado, porque ele não sabe o que é desejado e é menos surpreendente para a base de usuários.

No entanto, você está lidando com datas com muitos "padrões". Às vezes, eles até se misturam em um único programa (cadeia). Como você sabe que uma data é esperada, tentar reconhecê-la é apenas um bom design. Especialmente se a data vier de algum programa externo e passar sem modificação por um segundo a caminho.

Spencer Rathbun
fonte
2

Os programas implantados no servidor, na maioria das vezes, devem receber milhares de solicitações a cada minuto, ou às vezes a cada segundo. Se um programa do servidor aceitar e corrigir as entradas defeituosas dos clientes, receio que ele tenha 2 desvantagens:

  1. Perda de tempo precioso do servidor. Com mais de 1000 solicitações por segundo, a verificação de falhas em cada solicitação pode refletir uma resposta lenta para cada cliente.
  2. Injusto para o cliente / cliente-programas que fornecem entrada correta. Fora isso, quando um programador do servidor fica no código do servidor, ele precisa pensar nos vários casos de quais entradas defeituosas podem ser. Quem vai decidir isso?

Os programas de servidor não devem aceitar entrada com defeito, mas os servidores devem retornar uma mensagem de erro ao cliente, se houver uma entrada com defeito.

Abhishek Oza
fonte
2

O comportamento ideal, conceitualmente, é fazer o que pode ser feito com segurança, garantindo simultaneamente que alguém que possa resolver algum problema seja notificado de alguma forma. Na prática, é claro, muitas vezes é impossível encontrar essa última restrição diretamente e, portanto, torna-se melhor a questão de lidar com informações duvidosas.

Uma coisa que pode ser muito útil no design de um protocolo, especificação de formatação ou "idioma" é ter um meio de distinguir quatro categorias de itens potenciais não compreendidos:

  1. Coisas que devem ser filtradas se não forem entendidas.
  2. Coisas que devem ser ignoradas se não forem entendidas, mas ainda assim retidas se os dados precisarem ser repassados ​​(talvez em algum tipo de invólucro para indicar que eles passaram por pelo menos um estágio que não os entendeu)
  3. Coisas que devem gerar um aviso se não forem entendidas, mas não devem impedir uma tentativa de recuperação de dados (por exemplo, em uma página da web, um objeto cujo tipo é desconhecido, mas cujo final no arquivo é bem definido, pode ser renderizado como um vermelho "X" sem impedir que o restante da página seja renderizado.)
  4. Coisas que indicam que qualquer coisa que não possa entendê-las pode ter problemas graves e irrecuperáveis ​​em outros lugares (por exemplo, um indicador de que os dados restantes estão compactados e que qualquer coisa que possa ser entendida por qualquer coisa que possa executar a descompressão necessária).

Ter uma convenção bem definida pela qual aplicativos que possam ler qualquer versão de um formato de dados poderão reconhecer qual categoria é apropriada para qualquer coisa gerada em conformidade com versões posteriores é uma abordagem muito melhor do que tentar adotar medidas de compatibilidade ad-hoc mais tarde. Por exemplo, se um formato de arquivo tiver linhas com o formato "Tag: Value", é possível especificar que o primeiro caractere de qualquer tag indique a categoria em que pertence; para tags das categorias de ignorar silenciosamente, é possível que o primeiro caractere indique também a versão do padrão para o qual a tag deve ser válida (de modo que, se uma tag "ignorar silenciosamente" afirmar estar presente na versão 3 de o padrão, um analisador para a versão ignorá-lo-ia silenciosamente, mas um analisador para a versão 3 ou posterior gritaria se não pudesse analisá-lo).

O mais importante, em qualquer caso, é evitar a conversão de dados ambíguos ou incompreendidos em dados errados. Em alguns casos, pode ser melhor recusar a propagação de dados ambíguos, embora em outros casos seja melhor propagá-los exatamente como recebidos, caso o destinatário os considere ambíguos. O que é verdadeiramente perigoso, senão o mal, é a conversão de dados usando suposições variadas, por exemplo, a conversão de uma lista de datas como:

01/12/12
13/12/12
99/12/12

em uma lista de datas como

12/01/2012
13/12/2012
1999-12-12

Mesmo que alguém tenha uma lista com algumas datas inseridas erroneamente, pode ser possível para um ser humano, dada uma lista no formato original, determinar qual formato estava correto e sinalizar valores duvidosos para pesquisas adicionais (verificação em outros registros etc.) ) A renderização das datas no último formato, no entanto, seria irremediavelmente e irrecuperavelmente distorcida.

supercat
fonte
Bem dito. Eu gosto de como sua resposta é um pouco mais profunda. Fico tentado a aceitar isso, mas, dado o interesse geral que essa pergunta recebeu, acho que deixarei isso por um tempo.
Roman Starkov
1

Minha experiência de interface do usuário vem principalmente de sistemas de desktop. Os sites são diferentes, embora eu tenha visto alguns sites que podem desafiar um sistema de desktop. Mas pelo que vale a pena:

Descobri que as mensagens de erro devem ser o último recurso; um sistema ideal não os teria. A melhor coisa a fazer é não permitir entradas inválidas: o usuário não pode inserir "verde" se estiver selecionando uma lista suspensa de meses. Ele não pode pressionar um botão acinzentado.

A próxima melhor coisa a fazer é aceitar os dados incorretos. Digamos que você esteja exibindo um histograma de vendas diárias durante um mês. Após a entrada do usuário, o gráfico abrange um século e a barra de um século é 10 vezes maior que os outros. O usuário agora sabe que fez algo errado e, além disso, sabe muito mais sobre o que fez de errado do que qualquer mensagem poderia lhe dizer. Quando a entrada é gráfica - arrastando um mouse, por exemplo - esse tipo de feedback ainda funciona e é inestimável. Muitas entradas podem ser inválidas, mas, usando esse método, o usuário recebe feedback instantâneo e detalhado sobre os resultados de cada posição do mouse.

Tudo isso dito, às vezes o usuário precisa saber por que o botão está acinzentado para que ele não possa pressioná-lo. Então não há nenhuma ajuda para ele (se não é , deixe-me saber), mas para ungray o botão e, quando ele clica sobre ele, dar-lhe uma boa explicação de por que o botão não está funcionando no momento.

RalphChapin
fonte
0

A declaração é sobre o envio de informações pela Internet. Uma das coisas com o envio de informações pela Internet é que nem sempre elas chegam ao alvo ou são fragmentadas.

Pieter B
fonte
0

Algo que parece estar esquecido aqui - quais são as consequências do fracasso?

Exibir uma página da web? Você deve fazer todo o possível para tolerar informações incorretas. Suas opções são exibir o que você pode ou gerar um erro. O último curso não fornece nada ao usuário e, portanto, deve ser apenas um último recurso, pois fornece ao usuário um resultado completamente inútil; seria muito difícil que um erro fosse pior que isso.

Por outro lado, se estiver pedindo o alvo de um Minuteman III, você rejeitará "Moscou" como entrada, pois é potencialmente ambíguo.

Loren Pechtel
fonte
Então, mesmo que você seja um programador e tenha escrito algum código estúpido, o sistema deve fazer o possível para mostrar algo, em vez de apenas parar e gritar "oi, você estragou tudo aqui (número da linha)"? Você não acha que é exatamente isso que leva a códigos incrivelmente ruins?
Roman Starkov
@romkyns: Você tem modos de depuração para esse tipo de coisa que é estrita sobre gritar sobre erros.
Loren Pechtel 12/07/2012