Hey, que tal pensar além do Try... Catch... Finally? (parte 4)

02/02/2012 14:51

E aqui estou para fechar esta série de posts. Vimos no primeiro a diferença entre erro e exceção, e no segundo tivemos um vislumbre da amplitude do assunto "Exception Handling". No último post tomamos contatos com jargões comuns em tratamento de exceções além de conhecer alguns anti-patterns.


Pensando além do Try... Catch... Finally. Finnally!


Se você acompanhou todos os demais posts da série, espero ter conseguido lhe dar embasamento suficiente para que as seguintes assertivas sejam válidas:


Assertiva 1
Tratamento de exceções pode ser tratado como um item de cross-cutting concerns, e como tal, pode tirar proveito de frameworks e libraries existentes no mercado (muitas delas "free").

Dica
Considere não reinventar a roda!


Assertiva 2
Dentro de uma empresa, a manipulação de exceções pode ser expressa em termos de políticas. Considere, portanto, envolvimento do usuário, administradores e demais interessados na solução para discutir tais políticas.

Dica
Pense sempre em flexibidade para configuração das suas políticas de manipulação de exceção!


Assertiva 3
Defina um plano para pensar na manipulação de exceções durante a fase de design de uma solução. O resultado deve ser uma estratégia consistente e comunicada para todos os envolvidos. Em alguns casos, exige alguma definição de processo entre departamentos (por exemplo, "em caso de exceção no sistema de câmbio a equipe responsável deve ser notificada, os administradores devem receber um log").

Dica
Análise de exceções podem trazer grandes oportunidades de avaliação e correção do processo como um todo!


Assertiva 4
Manipular exceções é um excercício de altruísmo.

Dica
Ajude a equipe de manutenção com o rastreamento da exceção e suas causas. Seja objetivo e direto!


Assertiva 5
Exceções fornecem os meios necessários para separar os detalhes sobre o que fazer quando algo fora do comum acontece a partir da lógica principal de um programa. Na programação tradicional, a detecção de erros, relatórios e manipulação conduzem frequentemente a código espaguete confuso.

Dica
Trabalhe de forma organizada e mantenha seu código limpo.


Alguns exemplos de Exception Patterns


Provavelmente, os três design patterns mais conhecidos para tratamento de exceções são:

Exception Shielding: Este padrão garante que o aplicativo não vazará informações confidenciais (nomes de servidores, caminhos físicos dos servidores etc.).

Exception Logging: Este padrão ajuda a diagnosticar e solucionar problemas de exceções, auditoria de ações do usuário, rastreamento de atividades maliciosas e outras questões de segurança.

Exception Translation: Este padrão descreve como quebrar exceções dentro de outras exceções específicas dentro de uma camada (inner exception) para garantir que elas realmente reflitam ações pertinentes dentro do contexto.

Perceba que todos os patters de manipulação de exceção apresentados se relacionam com as técnicas de tratamento de exceções apresentadas no post anterior. Por exemplo, para não deixarmos que informações confidenciais vazem usamos Replace Handler. Quebrar exceções dentro de outras exceções específicas pode ser feito via com um Wrap Handler. Notificar os usuários ou administradores do sistema pode ser conseguido com Propagate Exception. Além disse, devemos combinar essas técnicas sempre que necessário.


Estratégia de tratamento de exceções


O diagrama abaixo ilustra uma possível estratégia de tratamento de exceções entre as camadas de uma dada solução. Para chegar numa estratégia como essa, cada building block de sua arquitetura deve ser avaliado com critério.

ExceptionStategy

Embora os exemplos desta série de posts ilustrassem situações corriqueiras em linguagens de programação como Java e C#, considere avaliar práticas de manipulação de exceção em banco de dados, processos de ETL, componentes de mensageria, SOA e qualquer outra tecnologia que forneça suporte para isso.


Conclusão


Todas as práticas envolvidas na manipulação de exceções oferecem como primeiro objetivo a possibilidade de uma aplicação continuar sua execução sem gerar maiores impactos. Contudo, um ganho excepcional pode ser oportunizado na auditoria das falhas ocorridas, ou até mesmo com um suporte efetivo para análise pela equipe de manutenção. Nunca subestime esse potencial.

Tratar exceções é muito mais que codificar um simples Try... Catch... Finally, ao contrário, é uma atividade que requer planejamento, análise (design), implementação e estudo constante.
Wink

Arquitetura



Hey, que tal pensar além do Try... Catch... Finally? (parte 3)

01/02/2012 15:14

Muito bem! Vimos no primeiro post da série a diferença entre erro e exceção, e no último tivemos um vislumbre da amplitude do assunto "Exception Handling". Estamos preparados para avançar um pouco mais nessa jornada e tomar contato com os...

... Jargões comuns em tratamento de exceções


Quando temos que manipular exceções, devemos desenhar uma estratégia robusta e bem-planejada. Isso é vital durante a fase de concepção, e ajudará no momento de implementação a evitar riscos. Apenas para citar algumas situações: exposição de mensagens de erro que contenham informações sensíveis, ou sair da aplicação em um estado inconsistente gerando erros nos dados quando um exceção ocorre.

Veja a seguir alguns jargões utilizados quando falamos em manipulação de exceções.

Wrap handler

A técnica de encapsular ou cobrir (wrap) uma exceção com outra pode trazer a capacidade de enriquecer com mais informações o rastreamento de pilha (stack trace). Por outro lado, o stack trace pode ficar demasiadamente longo (e irritante). Na maioria das vezes apenas o rastreamento de pilha raiz é interessante.

Replace handler

Consiste em substituir uma exceção por outra. A princípio parece uma péssima ideia, mas pode fazer sentido em determinados contextos onde informações sensíveis precisam ser mascaradas e substituídas por mensagens customizadas.

Logging handler

Um manipulador de logging é capaz de formatar a exceção (mensagem, stack trace e demais informações) e gerar um registro que pode ser publicado em diversos meios (Event Viewer, banco de dados, e-mail, arquivo texto, entre outros) para posterior auditoria.

Fault Contract exception handler

Este tipo de manipulador de exceção é utilizado para validar os limites de um contrato de uso de um serviço, classe, entidade, etc.

Interception handler

Consiste em interceptar exceções utilizando técnicas de AOP. Existem alguns frameworks (Java e C#) capazes de realizar interceptação em runtime e adicionar comportamento comum.

Multiple catch blocks

Muitas linguagens de programação suportam múltiplos blocos de catch. Segundo o Princípio de Responsabilidade Única (ou SRP, oriundo do SOLID), nunca deve haver mais de um motivo para uma classe mudar. A seguir temos dois exemplos de multiple catch blocks, onde podemos observar no segundo uma indicação muito forte de que o método pode estar quebrando o SRP.

Propagate exception

Embora toda exceção deva ser tratada, nem sempre é possível tratar uma exceção no mesmo escopo do método cuja invocação gerou a exceção. Nessas situações, devemos propagar a exceção para um nível acima na pilha de chamadas. Também é conhecido como rethrow de uma exceção.

Fault Tolerance Engineering

Técnicas para construção de sistemas com tolerância a falhas, bem como seu gerenciamento durante o ciclo de vida de uma aplicação têm sido alvo de debates há muitas décadas, sendo tema de diversos livros e pesquisas acadêmicas. Por estar relacionado com o tratamento e manipulação de exceções (seja em fase de design ou implementação), requer especial atenção quando tratamos de situações que exigem alta disponibilidade ou confiabilidade. Geralmente sistemas com essa características são baseados em redundância, quase sempre exigindo componentes especiais e/ou algoritmos específicos.

Obviamente, existem outros termos utilizados. Mas agora que vimos os mais comuns, para finalizar por hoje, vamos (tentar) nos divertir com...

... O horror dos anti-patterns de tratamento de exceções


Veja alguns exemplos de anti-patterns retirados do java.net (com algumas modificações que tomei a liberdade de fazer):

-> Log and Throw: escolha uma das duas coisas, nunca as duas!

-> Catch Exception: capturar exceções do tipo Exception (tanto em Java quanto em C#, por exemplo) quase sempre denota que um plano de tratamento de exceções não foi pensado com o devido cuidado. Tratar a exceção mais genérica pode indicar pouco conhecimento das funções de suas classes (ou no pior caso, demonstrar um design ruim).

-> Throwing the Kitchen Sink: Lançar várias exceções verificadas do seu método em geral é algo bom, contanto que exista de fato possíveis cursos de ação que o chamador pode querer tomar dependendo de qual exceção foi acionada.

-> Catching Exception: como vimos no post anterior, cada vez que uma catching exception é codificada um panda morre em algum lugar do planeta.

-> Destructive Wrapping: muito cuidado ao fazer o wrap de uma exceção, dê significado e acrescente informações de valor indiscutível.

-> Log and Return Null: quase sempre uma péssima ideia.

-> Catch and Ignore: o On Error Go to Next modermo!

-> Throw from Within Finally: o bloco Finally existe para ser executado, certo? nada mais justo que deixá-lo em paz.

-> Multi-Line Log Messages: alguns arquivos de log pode atingir tamanhos absurdos (para não dizer ridículos), portanto, cuidado!

Arquitetura



Hey, que tal pensar além do Try... Catch... Finally? (parte 2)

31/01/2012 12:40

Vimos no último post desta série a diferença entre erro e exceção. Enquanto o primeiro é irrecuperável, o outro dá margem para tomarmos uma ou mais ações a fim de permitir que a aplicação continue em execução.

Entendendo isso, o termo tratamento de exceções ganha um sentido imediato. Tratar ou manipular uma exceção requer muito critério e atenção ao contexto. Afinal, existe decisão puramente técnica?


Essa não! Catching Exception de novo!


Quem já trabalhou dando manutenção e suporte a sistemas em produção entende o valor do tempo. Poucos minutos bastam para desgastar (às vezes de forma irrecuperável) a imagem de um produto em caso de atraso na correção de um erro. Grande parte das boas práticas de desenvolvimento visam justamente retornar valor de forma eficaz durante a manutenção. O custo total de propriedade de um software deve levar em consideração o seu custo de manutenção.

Isso posto, podemos aferir que utilizar uma estrutura de “tratamento de exceções” (dedos no ar fazendo pequenos movimentos para realçar as aspas aqui) como vemos a seguir não traz qualquer benefício.

Na maioria dos casos essas exceções – que foram literalmente “engolidas” – confundem os desenvolvedores com um comportamento errático. Se você não sabe como tratar uma exceção, simplesmente não a capture. Simples assim. Quer logar? Tudo bem, capture e dê um rethrow, pelo menos é mais honesto com quem der manutenção depois.


Melhorias e evoluções no tratamento de exceções


O uso de exceções é um poderoso mecanismo que separa o código funcional do tratamento de erros. Contudo, mesmo que o uso de exceções simplifique a detecção de falhas (trazendo certa elegância para o código de manipulação de exceções) esse mesmo arranjo pode levar à negligência na recuperação de problemas. Esse tipo de falha pode representar: perda de dinheiro; comprometimento da imagem de uma empresa; perda de credibilidade por parte do cliente; outros problemas imensuráveis (ridículos ou, em alguns casos, catastróficos).

As linguagens de programação modernas, como C# e Java (entre tantas outras), têm fornecido cada vez mais suporte a manipulação explícita de exceções. Quando uma restrição semântica é violada ou alguma condição de erro excepcional ocorre, uma exceção é lançada. Uma exceção pode ser implicitamente propagada até o método original de chamada.

No exemplo a seguir vemos uma das muitas possibilidades de tratar exceções em Java.


Cuidado, se você é suscetível a flames ou trata desenvolvimento de software como religião, o texto a seguir pode causar incômodos. Nesse caso, pule para o último tópico do post.


Em Java existem três tipos de classes que podem ser “lançadas” durante o tratamento de exceções: checked exceptions (exceções verificadas), unchecked exceptions (exceções não verificadas), e erros.


Checked exceptions are exceptions that must be declared in the throws clause of a method. They extend Exception and are intended to be an "in your face" type of exceptions. A checked exception indicates an expected problem that can occur during normal system operation. Some examples are problems communicating with external systems, and problems with user input. Note that, depending on your code's intended function, "user input" may refer to a user interface, or it may refer to the parameters that another developer passes to your API. Often, the correct response to a checked exception is to try again later, or to prompt the user to modify his input.

Unchecked exceptions are exceptions that do not need to be declared in a throws clause. They extend RuntimeException. An unchecked exception indicates an unexpected problem that is probably due to a bug in the code. The most common example is a NullPointerException. There are many core exceptions in the JDK that are checked exceptions but really shouldn't be, such as IllegalAccessException and NoSuchMethodException. An unchecked exception probably shouldn't be retried, and the correct response is usually to do nothing, and let it bubble up out of your method and through the execution stack. This is why it doesn't need to be declared in a throws clause. Eventually, at a high level of execution, the exception should probably be logged (see below).

Errors are serious problems that are almost certainly not recoverable. Some examples are OutOfMemoryError, LinkageError, and StackOverflowError.


Começamos a entrar no campo das elucubrações sobre tratamento de exceções e erros. Só para citar um exemplo, veja esse post sobre o tema e esse outro aqui.

Pesquisando a respeito você encontrará muitos debates sobre o assunto. Se sua pesquisa se estender a outras linguagens, você chegará a conclusão que existe um sem fim de possibilidades, recomendações, padrões e estratégias para desenhar e arquitetar o tratamento de exceções.


Então devo desistir do assunto?


Claro que não! Ninguém é melhor do que você para criticar e decidir o que faz sentido sobre um determinado assunto. O que pode ser considerado uma vantagem para um pode não ser verdade para outro. Tudo depende do contexto, cenário e atores envolvidos. Nos próximos posts apresentarei várias considerações e técnicas. Smiley piscando

Arquitetura



Hey, que tal pensar além do Try... Catch... Finally? (parte 1)

30/01/2012 16:06

Não adianta. Toda vez que preciso explicar para algum desenvolvedor o que significa de fato “tratamento de exceções” fico constrangido. Na verdade, é um constrangimento alheio, já que quase sempre a pessoa se mostra surpresa ao final de uma conversa desse tipo.

Até compreendo desenvolvedores menos experientes ficarem melindrados na hora de tratar exceções. Mas considero um absurdo alguém com anos de experiência (às vezes se auto-nomeando um "desenvolvedor pleno/sênior" ou "arquiteto de software/solução/sistemas/etc.") considerar que um Try...Catch...Finally é suficiente para liquidar o assunto. E olha que prefiro nem mencionar o que penso sobre os "try {} Catch(Exception ex) {}" da vida (sem qualquer código dentro do catch).

Frente a essa recorrente situação de ter que tocar no assunto "tratamento de exceções", resolvi escrever alguns posts. Não quero simplesmente despejar um monte de conceitos e padrões. Nem mesmo ser o algoz da ingenuidade (maléfica) de desenvolvedores e responsáveis por design e implementação que desconhecem ou nunca buscaram mais conhecimento a respeito. Acho mais interessante estimular um pensamento crítico sobre o tema.

Acho melhor começar falando... Não do início da história das linguagens de programação... Mas do meu início como desenvolvedor profissional aqui em São Paulo.


À honra do “On Error Resume Next”!


Minhas primeiras experiências profissionais em São Paulo foram com WDNA e a velha trinca: ASP, Visual Basic 6 e COM+. Não posso dizer que foi de todo ruim, mas boa parte do que aprendi na época com os desenvolvedores mais experientes serviram para fundamentar uma inquietação: codificar TEM que ser mais do que isso!

Para manter o foco no assunto deste post, vamos analisar as opções que tínhamos na época para tratar erros (ou exceções? hum, boa questão, não?). Veja como uma estrutura típica de tratamento de erros em Visual Basic se parecia:


Perceba que interessante, o Visual Basic antigo considerava qualquer problema em runtime como erro, não havendo portanto uma abordagem para tratamento de exceções. Era possível utilizar a palavra reservada Raise para lançar um erro de runtime para a chamada original. Outras três possibilidades estavam à disposição dos desenvolvedores.

DontTryThisAtHome

O On Error GoTo dava a opção de usar uma determinada rotina em caso de ocorrência de erro de runtime. A execução do programa "pulava" para um trecho de código assinalado com um label pré-definido.  Já o On Error GoTo 0 desabilitava qualquer manipulador de erro escrito dentro do procedimento ou função corrente. Mas todos os desenvolvedores de Visual Basic (não o .NET) eram unânimes em concordar que o mais transudo, o que realmente colocava um desenvolvedor no hall dos "caras fodões e corajosos" era o tal do On Error Resume Next.

O On Error Resume Next tinha a habilidade de especificar que, quando um erro de runtime ocorresse, a execução iria para a instrução imediatamente após a instrução onde o erro ocorreu. Além disso, o programa seguia sua execução normalmente, como se nada tivesse acontecido. What?!?

Não saberia descrever em poucas palavras a minha "alegria" quando, ao final de uma tarde inteira debugando um erro numa página de ASP clássico (ou ASP 3.0, como queira), encontrava um On Error Resume Next oculto em um arquivo de include. Ah, que emoção! E não vou ser hipócrita, já "resolvi" alguns erros de produção usando um simples On Error Resume Next (e o gerente ainda fazia questão de enaltecer seu sagaz recurso para o cliente enquanto eu tentava, discretamente, deixar claro que "não era para tanto").

Na época, considerando todas as características do Visual Basic (linguagem fracamente tipada, interpretada, dinâmica, orientada a eventos, entre outras peculiaridades), a forma como comumente os desenvolvedores tratavam erros (ou exceções) era aceita pelo statu quo. Sempre fiquei incomodado com isso, afinal, já havia programado em Delphi anteriormente, experimentando estruturas de tratamento de exceções como a seguinte:


Notei que frequentemente me pegava fazendo a mesma pergunta...


Isso é uma exceção ou erro?


Ainda que você não concorde com as definições a seguir, é bastante plausível considerar que temos ao menos duas causas distintas, cada uma com seus respectivos desdobramentos, que afetam a execução do "caminho feliz" de um programa. São elas: erro ou exceção, sendo que:

  • Erro é qualquer desvio em relação ao comportamento esperado do sistema ou do programa, que interrompe o funcionamento do mesmo;
  • Exceção é qualquer erro ou problema que pode ser manipulado/tratado de forma a permitir que o sistema ou programa continue sua execução.


Perceba que, conceitualmente, um erro significa que algo deu muito errado com seu programa, e isso é um motivo suficientemente bom para ele abortar sua execução. Enquanto que uma exceção é resultado de uma situação inusitada, mas que você antecipou como uma possibilidade, e portanto codificou algo a respeito. Nessa linha de raciocínio, uma divisão por zero é um erro, mas a tentativa de ler um arquivo e descobrir que ele não existe é uma exceção.

Sintetizando, notamos que: uma exceção é recuperável, mas um erro não. Essa percepção serve como fechamento para hoje. Wink

Arquitetura



Void Podcast: episódios #013 ao #016 disponíveis!

23/01/2012 13:15

Void Podcast #013 – Dorg(m)as
Dessa vez, um assunto polêmico, discutido sem polêmicas, sobre os fanatismos nossos de cada dia. O (receptivo [gerente]) Vinicius Quaiato (@vquaiato) prega a paz mundial e aceitação de todas as tecnologias – independente de credo, raça ou religião (ou seria dogmas?!). Enquanto isso, o (cantor acusativo) Elemar Jr (@elemarjr) fala sobre complexos estranhos com nomes esquisitos (Você é pato? Você é Gabriela?) e o (ex-barbudo, com cavanhaque [latin-lover wanna be]) Leandro Daniel (@leandronet) mostra sua predileção por empregos extremos (de planos funerários a “grandes instituições financeiras”).

Void Podcast #014 – Elucubrações, percepções e desatinos
Dessa vez, uma discussão acalorada (sem pretensões, preocupações, consequências ou conclusões) sobre “os rumos” do desenvolvimento de software. Na defesa (ou seria no ataque) dos CRUD nossos de cada dia, temos: (arrobinha) Vinícius Quaiato (@vquaiato) e seus argumentos automotivos; Leandro Daniel (@leandronet) e seu sapiente cavanhaque arquitetônico (latin lover [really?!]); e o orquestrador caótico Elemar Júnior (@elemarjr). Completando o time (de peso?!) de heróis nessa edição, temos o Master of Coders (ou seria puppets?!) Juan Lopes (@juanplopes) em uma participação mais que especial.

Void Podcast #015 – Adeus Void velho, feliz Refactoring novo!
Queríamos falar sobre o fim-do-mundo, sobre o espírito do natal, sobre bons velhinhos, loucuras, orgias e, claro, sobre a paz mundial. Mas, no fim, falamos sobre aquilo que achamos que entendemos: código. Com a ausência percebida (por que será?!) do (arrobinha) Vinícius Quaiato (@vquaiato) e seus argumentos automotivos; temos o “sábio barbudo (com cavanhaque) das montanhas” Leandro Daniel (@leandronet) e o esperançoso/resharper-wanna-use Elemar Júnior (@elemarjr) em um debate frenético sobre o refactoring nosso de cada dia. Em um quase “essa é minha vida”, falamos cruamente sobre o que fazemos e como fazemos. Um void simples, mas honesto. Pobre, mas limpinho. Não é uma mensagem “bonitinha” de natal. Nem mesmo uma reflexão sobre o novo ano (afinal, o mundo vai acabar).

Void Podcast #016 – Void de gordo
Dessa vez, vamos confessar (todas) as gordices nossas de cada dia. Para isso, contamos com o apoio de um grande (!?) especialista: Dudu Sales (@papodegordo) do famoso e premiado “Papo de Gordo” que honra-nos com sua notória (!?) presença.

Nesta edição, encontramos um comedido, respeitoso e (acreditem!) trollado Vinícius Quaiato (@vquaiato), tentando entender o universo gordo. Já que, sendo o rostinho bonito que atraí ouvintes ao Void, não teve tempo (ou garfo) para viver a vida como ela é nesse “mundo redondo” (aliás, fartamente representado [por enquanto] pelos nossos outros dois heróis).

Ouça e entenda alguns dos complexos do (agora) diabético, (ainda) gordo, pé-grande, (segundo alguns) careca, sem internet e, conforme o convidado, fudido Elemar Jr (@elemarjr) sobre os (des)confortos que apenas um “corpinho” pode proporcionar em aviões, lojas de calçado e outros ambientes com espelhos. Com a edição homérica (digna de lenda) do artista revoltado (sim! agora ele fala palavrões) Leandro Daniel (@leandronet), descanse um pouco e relaxe ouvindo um Void despretensiosamente hilário.

É possível ouvir o podcast diretamente do post (usando o player), além disso, o Void Podcast agora está disponível também no iTunes.

Não deixe de comentar suas opiniões. Wink

Podcasts , ,