Quando uma API não faz nada, faça esse nada do jeito certo
- Quando uma API não deve fazer nada, é importante garantir que ela não faça nada da maneira correta.
- Por exemplo, o Windows tem uma vasta infraestrutura de impressão, mas o Xbox não tem essa infraestrutura.
- Quando um aplicativo tenta imprimir no Xbox, lançar
NotSupportedException é a maneira errada de lidar com isso.
- Como o aplicativo provavelmente foi testado principalmente no PC, a exceção pode não ser tratada ao rodar no Xbox e o aplicativo pode travar.
- Um design melhor para "suportar" impressão no Xbox é a função de impressão ter sucesso, mas informar que não há impressoras instaladas.
- Quando o usuário tenta imprimir, o sistema pede a seleção de uma impressora, mas a lista está vazia, então o usuário percebe que "não há impressoras" e cancela a solicitação de impressão.
- Para aplicativos que tentam instalar uma impressora, a função de instalação pode retornar imediatamente com um código de resultado dizendo que "o usuário cancelou a operação".
- O objetivo é se comportar como se o recurso de impressão fosse totalmente suportado, mas na prática agir como se não houvesse impressoras.
- Em sistemas onde impressão simplesmente não funciona, pode-se adicionar uma função que verifique se a impressão está disponível para ocultar o botão de imprimir na UI.
- Esse comportamento é chamado de "inerte".
- A superfície da API continua existindo e funcionando conforme a especificação, mas na prática não faz nada.
- O importante é não fazer nada de forma consistente com o que está documentado, minimizando problemas com código existente.
Exemplo de desativação de API
- Há um exemplo de desativação de uma API que inclui várias funções para criar handles de widget, funções que recebem handles de widget e uma função para fechar esses handles.
- A equipe inicialmente propôs desativar a API fazendo com que
CreateWidget tivesse sucesso, mas retornasse um ponteiro nulo.
- Porém, essa abordagem pode confundir o aplicativo. "A chamada teve sucesso, mas eu não recebi um handle válido?"
- Fazer
EnableWidget retornar "handle inválido" também pode causar confusão.
- Na documentação existente, encontraram um valor de retorno chamado
ERROR_CANCELLED, que significa que a criação do widget foi cancelada pelo usuário.
- Portanto, sempre que o aplicativo tentar criar um widget, é possível responder: "não, o usuário cancelou".
Opinião do GN⁺
- O ponto mais importante deste texto é que, quando uma API não faz nada, ela deve não fazer nada de um jeito que não prejudique a experiência do usuário e mantenha a compatibilidade com o código existente.
- Essa abordagem reforça para os desenvolvedores a importância do design de APIs e contribui para oferecer uma experiência de software mais amigável ao usuário.
- O texto mostra um aspecto cuidadoso da engenharia de software e oferece um caso interessante sobre como aplicativos podem falhar de forma elegante até mesmo em ambientes inesperados.
2 comentários
Comentários do Hacker News
Opinião sobre “engolir erros”:
panicda linguagem Go é uma boa forma de sinalizar em alto e bom som erros do programador durante os testes.Opinião sobre retrocompatibilidade:
Reclamação sobre design de UI:
Crítica à falta de aprendizado da Microsoft:
Opinião sobre a falta de suporte a impressão no Xbox:
Conselho sobre uso de API:
NotSupportedException.Sentimento sobre “conformidade maliciosa”:
Opinião positiva sobre segurança:
Reflexão sobre a estratégia dos navegadores:
O mal-entendido dos críticos sobre tratamento de exceções:
Comentários no Lobste.rs
Brincadeiras à parte, não concordo com esse tipo de programação excessivamente defensiva e com essa experiência do usuário. Desse jeito, o software simplesmente deixa de fazer o que deveria sem que ninguém saiba por quê, e não há como descobrir o motivo. O app deveria capturar o erro e, se possível, gerar uma mensagem amigável para o usuário; caso contrário, ao menos mostrar a mensagem de erro original. Se for uma tarefa em segundo plano, deveria haver um log de erros
Reconheço que este texto foi escrito da perspectiva de um desenvolvedor de API, não de um desenvolvedor de aplicativo. Então, os erros da API devem ser documentados, e devem ser fornecidas mensagens de erro sobre as quais quem chama a API possa agir
Também não gosto de esconder botões na UI só porque o usuário não tem permissão. Se houver espaço, acho melhor mostrar o botão desativado e, quando o usuário passar o mouse por cima, exibir uma mensagem explicando como ativá-lo
No geral, é melhor exigir correção. Mas, se você tem 1 bilhão de usuários existentes, é muito sensato evitar quebrar as coisas sempre que possível, e isso também gera valor real no nível do sistema porque, do ponto de vista do usuário, simplesmente funciona. No fim, a atitude deveria ser: falhe rápido, mas não faça com que muita coisa falhe
Isso está mais próximo de estabilidade de ABI do que de API. No Windows, até software compilado há 15 anos deve continuar funcionando em sistemas operacionais novos sempre que possível. Como não dá para mudar a assinatura da função, se você quiser que APIs que já não fazem mais sentido continuem funcionando, precisa contar mentiras piedosas
Por exemplo, a API ainda finge que o Active Desktop existe, porque a alternativa seria quebrar em massa softwares antigos já existentes
Fica impossível saber se a função foi removida ou se acabou escondida em outra tela nesse meio-tempo
Mas, se o app não fizer isso, as pessoas que usam esse app vão culpar o Windows. Vão culpar o Windows, não o app, e isso vale até quando o app trava
É por isso que a Microsoft cria soluções alternativas. É muito mais fácil deixar o trabalho de impressão simplesmente desaparecer, e aí o usuário pensa por um instante e aceita: “ah, é verdade, não tem impressora”
ifpara checar null. Sempre que vejo isso, lembro deste textoEntão passei a instruir o agente a não repetir verificações de null, a usar funções inofensivas e a verificar uma única vez, no momento da declaração, que o valor nunca é null