sexta-feira, 6 de dezembro de 2013

Propagação Deficiente - Antipattern de exceção

Para entender melhor este antipattern, é necessário entender o que compõe uma exceção.

Exceção Exemplo #1
ApplicationException: Erro ao carregar configuracoes
    at LeitorConfiguracoes.lerArq(LeitorConfiguracoes.java:404)
    at DeamonAplicacao.iniciar(DeamonAplicacao.java:376)
    at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.NullPointerException
    at LeitorConfiguracoes.parser(LeitorConfiguracoes.java:120)
    at LeitorConfiguracoes.lerArq(LeitorConfiguracoes.java:386)
    ... 2 more
Identificando os componentes da exceção:
  • Classe: ApplicationException
  • Mensagem: Erro ao carregar configuracoes
  • Stack Trace: desde LeitorConfiguracoes.lerArq, até java.lang.Thread.run
  • Caused by: é a nova exceção a partir da linha "Caused by" (inclusive)
Lembrando que a mensagem, e o "caused by" não são obrigatórios.
Veja o outro exemplo abaixo:
Exceção Exemplo #2
java.util.NoSuchElementException
    at LeitorConfiguracoes.parser(LeitorConfiguracoes.java:124)
    at LeitorConfiguracoes.lerArq(LeitorConfiguracoes.java:386)
    at DeamonAplicacao.iniciar(DeamonAplicacao.java:376)
    at java.lang.Thread.run(Thread.java:722)
Identificando os componentes da exceção:
  • Classe: java.util.NoSuchElementException
  • Mensagem: não tem (ela deveria estar na frente do nome da classe da exceção)
  • Stack Trace: desde LeitorConfiguracoes.parser, até java.lang.Thread.run
  • Caused by: não tem
Com base nos exemplos acima, agora mostro o erro mais comum, quando se trata de exceção:
catch (Exception e) {
    log.error(e.getMessage());
}
Ou
catch (Exception e) {
    throw new ApplicationException(e.getMessage());
}
Para a Exceção Exemplo #1, dos 4 elementos que tem a exceção, a expressão "e.getMessage()" representa apenas "Erro ao carregar configuracoes". Ela seria logada da seguinte forma:

17:52:37 [ERROR] Erro ao carregar configuracoes
Somente com esta informação, como é possível fazer um trobbleshooting eficiente? A classe, a Stack Trace e o Caused By são MUITO importantes. A mensagem é apenas um fragmento da exceção.
Para a Exceção Exemplo #2, é muito pior. A exceção não tem mensagem. E apenas encontraríamos uma linha perdida no log com a seguinte aparência.
17:52:37 [ERROR] null
Uma linha assim não ajudaria em nada, praticamente.
Então lembre-se:

  • Em um log de erro, o primeiro parâmetro é uma mensagem, e o segundo parâmetro a exceção (com isso, todos os componentes da exceção são logados)
  • Se for encapsular e lançar a nova exceção, em geral, existe um construtor da exceção onde o primeiro parâmetro é uma mensagem, e o segundo parâmetro é a exceção (parâmetro cause)

catch (Exception e) {
    log.error("Erro na operacao", e);
}
Ou
catch (Exception e) {
    throw new ApplicationException("Erro na aplicacao", e);
}

Captura e Ignora - Antipattern de exceção

Em qualquer sistema, as exceções sempre irão existir, e por motivos diversos:
  • a importação de um arquivo com um dado não esperado, 
  • uma sobrecarga no sistema que provocou um timeout em uma operação, 
  • uma tabela que esqueceram de atualizar no banco, 
  • o disco que encheu, 
  • um arquivo com permissão de somente-leitura,
  • um possível bug,
  • etc etc etc.
Estas ações são indesejáveis e, por mais exaustivamente que se teste o sistema, você nunca terá a garantia que estará livre dos problemas acima. Você dificilmente conseguirá sempre realizar testes para todas estas condições e tantas outras não listadas.
Mas o que temos que fazer afinal?
Tratar a exceção da melhor maneira possível, informando a exceção com precisão no log, e passando uma mensagem precisa ao usuário. Mas as vezes encontramos exatamente o contrário, trechos de código que captura a exceção, e segue em frente como se nada tivesse acontecido.
Não faça isso com sua exceção
Abaixo estão alguns exemplos do que NÃO se deve fazer:
catch (IOException e) {
  return null;
}
Ou
catch (IOException e) { }
Com isso, além de simplesmente retornar nulo (ou não fazer nada), ao invés de manipula-la, ou relançar a exceção, você joga fora de vez qualquer informação referente ao problema que gerou a exceção.
Outras maneiras de esconder a exceção:
catch (IOException e) {
    log.error("Erro", e);
    return null;
}
Ou
catch (IOException e) {
    e.printStackTrace();
    return null;
}
Ou

catch (IOException e) {
    e.printStackTrace(); // Ou log.error("Erro", e);
}
De maneira similar as situações anteriores, a única diferença é que a exceção, pelo menos, foi logada, mas o fluxo de execução continua normalmente. Isto nem sempre está errado, mas muitas vezes o programador não insere este código nos pontos apropriados. Ao invés de retornar nulo ou seguir com o fluxo de execução, a exceção deveria ser tratada.

quinta-feira, 28 de novembro de 2013

Logar e Relançar - Antipattern de exceção


Quando se trata uma exceção, o correto é:
  • Ou envolver a exceção em uma nova exceção, passando mais informações;
  • Ou tratá-la, logando a exceção e exibindo ao usuário, quando for o caso. 
Mas nunca os dois (relançar e logar). Considere o Servlet abaixo. Neste exemplo, este é o nível mais alto da operação, então o papel do tratamento de exceção é informar ao usuário (logar e enviar uma mensagem para o Response).

public class ClienteServlet ...
    ...
    public void gravarCliente(ServletRequest req, ServletResponse res) {
        try {
            DAOCliente.gravar(criaClienteVo(req));
        } catch (RuntimeException e) {
            log.error("Erro ao salvar cliente", e);
            res.setAttribute("error", "Erro. Contate suporte.");
        } catch (BusinessException e) {
            log.error("Erro de negócio ao salvar cliente", e);
            res.setAttribute("error", "Erro ao salvar cliente " 
                    + e.getMessage());
        }
    }
}    

Exemplo de Antipattern 1

No caso de um código que loga e relança, como o código abaixo, teremos a saída mostrada na sequência. Note que a exceção é mostrada 2 vezes, mas de maneiras diferentes. 

public class DAOCliente ...
    ...
    public void gravar(ClienteVo cliente) throws BusinessException {
        try {
            ...
            ... COMANDO INSERT
            ...
        } catch (SQLException e) {
            log.error("Erro ao salvar cliente", e);
            throw new BusinessException("Erro no banco", e);
        }
    }
}

13:40:02 [ERROR] Erro ao salvar cliente
java.sql.SQLException: ORA-00001: unique constraint ...
    at oracle.driver...SomeOracleDriverClass()
    at DAOCliente.gravar(DAOCliente.java:50)
    at ClienteServlet.gravarCliente(ClienteServlet.java:85)
13:40:02 [ERROR] Erro de negócio ao salvar cliente
BusinessException: Erro no banco
    at DAOCliente.gravar(DAOCliente.java:63)
    at ClienteServlet.gravarCliente(ClienteServlet.java:85)
Caused by: java.sql.SQLException: ORA-00001: unique constraint ...
    at oracle.driver...SomeOracleDriverClass()
    at DAOCliente.gravar(DAOCliente.java:50)
    ... 2 more

Exemplo de Antipattern 2

Ou ainda como o código abaixo, exatamente a mesma exceção é logada 2 vezes.

public class DAOCliente ...
    ...
    public void gravar(ClienteVo cliente) {
        try {
            ...
            ... COMANDO INSERT
            ...
        } catch (SQLException e) {
            log.error("Erro ao salvar cliente, e);
            throw new e;
        }
    }
}

13:40:02 [ERROR] Erro ao salvar cliente
java.sqlSQLException: ORA-00001: unique constraint ...
    at oracle.driver...SomeOracleDriverClass()
    at DAOCliente.gravar(DAOCliente.java:50)
    at ClienteServlet.gravarCliente(ClienteServlet.java:85)
13:40:02 [ERROR] Erro de negócio ao salvar cliente
java.sql.SQLException: ORA-00001: unique constraint ...
    at oracle.driver...SomeOracleDriverClass()
    at DAOCliente.gravar(DAOCliente.java:50)
    at ClienteServlet.gravarCliente(ClienteServlet.java:85)

Código esperado

O código deve OU relançar uma exceção (caso seja um nível intermediário ou inferior) OU logar e informar ao usuário (caso seja o nível superior). No exemplo abaixo, como não é a classe de nível superior, ela deve apenas relançar a exceção. Com isso, o log fica com apenas uma única ocorrência.

public class DAOCliente ...
    ...
    public void gravar(ClienteVo cliente) {
        try {
            ...
            ... COMANDO INSERT
            ...
        } catch (SQLException e) {
            throw new BusinessException("Erro no banco", e);
        }
    }
}

13:40:02 [ERROR] Erro de negócio ao salvar cliente
BusinessException: Erro no banco
    at DAOCliente.gravar(DAOCliente.java:63)
    at ClienteServlet.gravarCliente(ClienteServlet.java:85)
Caused by: java.sql.SQLException: ORA-00001: unique constraint ...
    at oracle.driver...SomeOracleDriverClass()
    at DAOCliente.gravar(DAOCliente.java:50)
    ... 2 more

terça-feira, 19 de novembro de 2013

Exceção Java, torne-se sua aliada

Quem é que gosta quando a sua aplicação retorna com alguma exceção? Acho que ninguém, não é mesmo? E um atendente que está em um atendimento telefônico tentando registrar uma solicitação do cliente no sistema e acontece algum erro? O atendente e o cliente também não vão gostar nem um pouco de receber a exceção.
Sentimento do usuário quando verifica uma mensagem genérica de erro

E o que deve ser feito? Deve mostrar o problema claro e preciso possível para o atendente ou para o operador, para se tenha total condição de resolver o problema o quanto antes. 
Sempre que acontece um erro, a JVM gera uma classe que contém as informações sobre o problema. Esta classe estende Throwable. Existem 3 tipos destas classes:

  • Exceções verificadas (checked exceptions) - exceções que devem ser tratadas na cláusula throws de um método ou catch de um bloco try. Estas exceções estendem a classe Exception. Problemas de comunicação com sistema externo, ou problemas de entrada do usuário, são alguns dos exemplos destas exceções. 
  • Exceções não-verificadas (unchecked exceptions) - exceções que não devem ser tratadas na cláusula throws de um método ou catch de um bloco try. Estas exceções estendem a classe RuntimeException. Em geral são problemas não esperados, podendo até mesmo indicar um bug no sistema. O mais comum é o NullPointerException
  • Erros - são problemas na JVM que, em geral, não são recuperáveis. Estendem a classe Error. Exemplo: OutOfMemoryError, LinckageError, e StackOverflowError
Mesmo as vezes sendo Error, e estendendo Throwable, o conjunto de todas estas classes, normalmente são chamadas de Exceções (ou Exception).
Diagrama de classes referente as exceções (Throwable)

E é importante entender que a exceção (ou Throwable) tem 4 componentes e TODOS eles são importantes:

  1. Classe - indica qual tipo de erro ocorreu
  2. Mensagem (opcional) - detalha o erro com uma mensagem, quando for o caso
  3. Stack Trace - indica em que ponto do código aconteceu o erro, e toda a pilha de métodos que foi chamada para chegar neste ponto
  4. Cause (opcional) - indica qual foi a exceção original, quando for o caso

segunda-feira, 11 de novembro de 2013

Boas práticas para o uso de logs em Java

O JDK vem com o pacote java.util.logging que tem a biblioteca para tratamento de log nativa e o Log4J do projeto Apache é a opção log mais utilizada. O Log4J tem mais features que o java.util.logging. O Common Logging também da Apache é apenas um wrapper (uma casca) para encobrir o uso de qualquer mecanismo de log, inclusive o Log4J e java.util.logging.

Uma mensagem em qualquer mecanismo de log pode ter níveis de severidade diferentes. Estes níveis se dividem basicamente em:

  • fatal - este nível deve ser utilizado em casos extremos, em geral mensagens que impacta negativamente todo o ambiente (e não apenas uma sessão de um usuário) e exige uma ação não de um operador, mas sim do administrador do ambiente e do arquiteto do sistema
  • error - este nível representa uma exceção Java que ocorreu durante a execução de uma operação de um usuário ou a execução de uma tarefa. Pode-se enviar mensagens neste nível para alguma inconsistência identificada pelo próprio sistema, mesmo que não gere exceção. 
  • warn - este nível é utilizado para alguma informação que seja interessante ser registrada, que pode representar um problema, mas o sistema se recuperou e não deixou de executar a tarefa.
  • info - este nível de mensagem é utilizado para indicar o que é que a aplicação está fazendo em alto nível. 
  • debug - este nível de mensagem, são de baixo nível, onde detalha resultados intermediários de uma tarefa, e são utilizados, normalmente para depuração de um trecho da aplicação
Ata da Reunião - cada anotação tem uma severidade diferente
Não se deve usar a saída padrão (System.out) ou a saída de erro (System.err). Deve sempre usar o log. O log é configurável, você habilita e desabilita seus níveis, permite escrever em diversos destinos diferentes (console, arquivo, banco de dados, rede, etc), seleciona o nível por pacote Java, permite rotacionar, informar data e hora para toda mensagem, entre outras vantagens. Escrever na saída padrão ou de erro não permite nada disso, o que pode tornar a informação volumosa e confusa. 

Quando na aplicação se escreve em níveis mais baixo, principalmente o nível debug, é importantíssimo utilizar isDebugEnabled() por questão de otimização. É normal que, quanto menor o nível, mais mensagens existam, mas é normal que você não queira ver as mensagens de log de todos os módulos da aplicação, mas apenas de alguns. Então deve-se habilitar o log apenas destes módulos.

log.debug("Retorno cliente " + cliente + ", Status=" + status);

No exemplo acima, sempre irá concatenar a string, para chamar o método log.debug, para verificar se está habilitado ou não o log para este pacote Java. Caso esteja, escreve no log normalmente, mas caso não esteja, o Java realizou a concatenação da string desnecessariamente. Agora imagine este tipo de código em um loop e em diversos pacotes diferentes, sendo que o modo debug não está ligado. É uma concatenação desnecessária que ocorre com todos os log.debug. Por questão de otimização, deve-se verificar o nível de log antes de concatenar a string do debug.


if (log.isDebugEnabled()) {
    log.debug("Retorno cliente " + cliente + ", Status=" + status);
}

Logando exceção

Quando o código encontra uma exceção, ele deve:

  • tratar a exceção - se seu código pode tratar a exceção (ex.: falha de rede), então deve fazê-lo; ou
  • relançar a mesma exceção - em caso de exceção unchecked; ou
  • reencapsular a exceção - no caso de exceções checked; ou
  • logar e exibir ao usuário - se em nenhum outro ponto foi possível tratar a exceção, em geral no nível mais alto da stack trace (na Action, ou onMessage, ou no método main), então a mensagem deve ser logada e informada ao usuário.
Sempre que reencapsular a exceção, a nova exceção precisa fazer bom uso dos dois parâmetros que existem nos construtores das exceções:
  • message - informar a atividade que estava tentando ser realizada quando a exceção ocorreu, preferencialmente informando algum parâmetro de entrada para ajudar a identificar o erro; e
  • cause - é a exceção original
Um exemplo de como relançar a exceção pode ser conferido a seguir:


void salvar(Cliente c) {
    try {
        DAOCliente.salvar(c);
    } catch (SQLException ex) {
        throw new AppException(
            "Erro de banco ao salvar cliente " // ação com erro
            + c != null ? c.getNome() : null, // um parâm. de entrada
            ex); // exceção original
    }
}

Da mesma maneira, estes dois parâmetros são de extrema importância durante o log no nível mais alto de erro:

void doGet(HttpServletRequest req, HttpServletResponse resp) {
    try {
        // . . .
        businessClient.salvar(c);
        // . . .
    } catch (AppException ex) {
        log.error(
            "Erro ao tentar cadastrar cliente " // ação com erro
            + req.getParameter("nome"), // um parâmetro de entrada
            ex); // exceção original
    }
}


quarta-feira, 30 de outubro de 2013

Tecnicas de Levantamento de Informações

  1. Entrevista
    • É a interação com o Cliente/Usuário. Consiste na seguinte lógica de organização que possibilita em ótimos resultados:
      • Preparação da entrevista: É importante estudar o material disponível, estabelecer um principal objetivo da entrevista e preparar uma lista de questões.
      • Condução da entrevista: Falar menos que o entrevistado e se possível levar um escriba (responsável por registrar os pontos importantes da entrevista).
      • Finalização da entrevista: Reservar alguns minutos pra resumir e consolidar os principais pontos discutidos/citados, informar quais serão os próximos passos e que será enviado uma ata (explicar que, após aprovado será publicada).
  2. Pesquisa a Manuais e Registros
    • Internos
      • Manuais
      • Relatórios
      • Formulários
      • Bases de Conhecimento
    • Externos
      • Publicações especializadas do negócio
      • Estatísticas governamentais
      • Relatórios de pesquisa e tendências
      • Vendedores, Clientes
      • Internet, Bases de Conhecimento
  1. Questionário
    • É usado geralmente para coletar informações de um grupo, departamento ou até mesmo individualmente. São informações solicitas por escrito, quando se tem dificuldade pra reunir os stakeholders por exemplo.
    • Um bom questionário deve ser bem estruturado e deve ser de fácil entendimento, com perguntas claras e objetivas, por isso exige um bom tempo para criá-lo.
    • Preferencialmente, antes de envia-lo é ideal que faça um teste com algumas pessoas, a fim de obter opiniões e qualidade do mesmo.
  2. Observação
    • Utilização do método "In Loco"
      • Esse processo consiste em acompanhar de perto todos os passos e processos da tarefa em questão. 
    • As principais dúvidas a serem esclarecidas são:
      • Qual o escopo/objetivo do trabalho
      • Qual o resultado final, o que se espera como resultado final?
      • Quem executa cada passo do trabalho?
      • Com quais recursos?
      • Como se faz?
      • Quando se inicia este trabalho e quais são as entradas
      • Quem solicita?

  3. Role Playing
    • Analistas
      • É ideal aprender todo o trabalho e processo (fim a fim) do usuário
    • Vantagens
      • Levanta informações ainda não mapeadas nas demais tecnicas e obtem maior domínio do problema.
      • Conhece os problemas que geralmente os usuários enfrentam.
  4. Brainstorming

    • Grupo de no máximo 10 pessoas (aconselhável) reunidas, trocando informações e idéias com a finalidade de resolver algum problema/propor novas soluções. Quanto maior o número de ideias, melhor vai ser o resultado.
      • O objetivo precisa ser bem definido
      • Modificar as ideias ou combina-las pode ter bons resultados.
      • Debates ou críticas não podem ser permitas, a finalidade é escutar a todos, independente da opinião.
  5. Storyboard
    • São dados gráficos, que tem a finalidade de ajudar a mostrar/ilustrar o processos e ou passos do trabalho, de forma sequencial. Normalmente mostra quais são os atores, quando se inicia cada passo e são quais os objetivos.
  6. Workshop
    • Conduzido por um facilitador (geralmente de 1 a 5 dias), tem a finalidade de se chegar num consenso sobre escopo, riscos e caracteristicas (Casos de Uso, Requisitos, Regras de Negócio, etc).
      • Obs: As decisões são escritas e aprovadas na hora. As mesmas são enviadas a todos os participantes.
  7. JAD (Joint Application Design)

    • Consiste numa dinamica de grupo, que tem como objetivo definir os objetivos e requisitos de um projeto/sistema.
    • Recursos visuais são muito usados e tem como finalidade estimular os sentidos, criar interesses, esclarecer alguns pontos confusos e otimizar o tempo da dinamica. Consegue definir objetivos com mais eficaz, evitando retrocessos.
    • Toda documentação é gerada durante as sessões, assim é possível que os usuários entendam o porque das decisões feitas podendo discutir o conteúdo criado.
    • Cerca de 20 a 60% de melhora na produtividade para especificação de requisitos, se comparado com outros métodos tradicionais.
  8. Protótipos
    • É a demonstração de um modelo ou comportamento de um projeto/solução, muito útil e recomendado para:
      • Obter feedback do cliente
      • Validar alguns requisitos
      • Demonstrar entendimento do escopo
      • Obter aprovação da solução
    • OBS: É importante deixar claro para o cliente que se trata de um protótipo (um esboço desenhado numa folha de papel é muito utilizado por alguns analistas), e não do sistema em si. O intuito é dar um esboço e validar os pontos citados anteriormente. 

terça-feira, 29 de outubro de 2013

Termo de Abertura de Projeto (TAP) - Project Charter (PC)

termo de abertura do projeto (TAP) ou Project Charter (PC) em inglês, é o documento que autoriza formalmente o projeto. Ele concede ao gerente/gestor a autoridade para utilizar os recursos da organização na execução das atividades do projeto.
O termo de abertura do projeto deve abordar, ou referenciar, as seguintes questões:
  • requisitos que satisfazem as necessidades do cliente;
  • objetivos do projeto (que devem ser SMART);
  • propósito ou justificação do projeto;
  • stakeholders do projeto e os seus papéis e responsabilidades;
  • expectativas dos stakeholders;
  • identificação do gestor do projeto. e nível de autoridade do gerente;
  • cronograma macro dos marcos do projeto;
  • premissas, ou pressupostos, organizacionais (fatores considerados verdadeiros, reais ou certos);
  • restrições organizacionais (fatores que limitam as opções da equipe);
  • investimento (orçamento preliminar);
  • constrangimentos e riscos;
  • descrição do sub-produto(s) identificados;
  • milestones identificadas.

Permite assim responder a questões como:

  • O que deve ser feito para atingir o objectivo do projeto?
  • Como deve ser feito?
  • Quem o vai fazer?
  • Quando deve ser feito?

Introdução ao Gerenciamento de Projetos

A partir deste momento estaremos detalhando e apresentado assuntos inerentes à projetos, onde o ponto objetivo é expandir o conhecimento sobre o assunto, no qual o mesmo não é aplicado apenas na área de TI e que na prática a aplicabilidade de projetos é difundida para diversas áreas e até em nossas vidas.

Gestão de projetos ou gerência de projetos ou gerenciamento de projetos ou administração de projetos é a área da administração aplicada de conhecimentos, habilidades e técnicas na elaboração de atividades relacionadas para atingir um conjunto de objetivos pré-definidos, num certo prazo, com um certo custo e qualidade, através da mobilização de recursos técnicos e humanos.


O que é um Projeto?

Um Projeto é normalmente definido como um empreendimento colaborativo ou como um esforço temporário empreendido para criar um produto, serviço ou um resultado exclusivo. Um projeto frequentemente envolve um pesquisa ou desenho, que é cuidadosamente planejado e organizado para alcançar um objetivo particular.

Ciclo de Vida de um Projeto

O Ciclo de vida de um projeto é a estimativa de duração de um projeto indeterminado, que possui início, meio e fim. O Ciclo de Vida do projeto serve para detalhar as fases do projeto de forma que o mesmo apresente uma organização e coerência em sua estrutura. Cada fase do projeto é marcada pela entrega (entrega = deliverables "em inglês") de um ou mais produtos/serviços, como estudos de viabilidade ou protótipos funcionais.O início de cada fase define o trabalho a ser realizado por todos os envolvidos em sua execução. O final de cada fase é marcado por uma revisão do produto/serviço e do projeto até o momento em que o mesmo se encontra. Quando há "overlapping" entre as fases, denominamos essa prática de "fast tracking", nesse caso, começa-se a trabalhar nas próximas fases do projeto antes do fim da fase corrente (entrega e revisão dos produtos). Os custos de um projeto são geralmente crescentes à medida que a fase avança. Os riscos são geralmente decrescentes à medida que a fase avança. A habilidade das partes envolvidas alterarem os produtos de cada fase é decrescente à medida que a fase avança. As fases de um projetos são descritas como: 
Iniciação, Planejamento, Execução, Monitoramento ou Controle, Finalização. 

Conforme a imagem abaixo:










Componentes de um Projeto

  • Equipe
  • Gerente
  • Cliente
  • Fornecedores
  • Stakeholders/Shareholders (qualquer pessoa ou organização que tenha interesse, ou seja afetado pelo projeto, partes envolvidas no projeto).

segunda-feira, 28 de outubro de 2013

Antipatterns na manipulação de exceções e logs

Ninguém constrói um software acreditando que ele irá falhar, mas, pelo menos, deveríamos prepará-lo caso isso ocorra.

Se o Titanic não foi construído para afundar, então para que se preocupar com avisos de emergência e botes salva-vidas, por exemplo. Mas se acontecer algum problema em seu casco a ponto de entrar mais água do que ele pode suportar, ele irá parar no fundo do oceano, queira você ou não. Então coloque alarmes no barco e botes salva-vidas para todos.

E com o seu software não é diferente. Se uma package no banco ficar inválida, ou se o disco do servidor encher porque alguém esqueceu de monitorar, a sua aplicação irá parar, queira você ou não. E não é o famoso "Erro no sistema. Contacte o administrador" que irá salvar você. Emita um aviso preciso, ou o sistema ficará por horas parado até encontrar qual é o problema.
A maior catástrofe marítima de todos os tempos. Todo software pode apresentar problemas e esteja preparado quando isso acontecer para não ser mais um Titanic. 
Pode parecer boba a comparação, mas, infelizmente, não são apenas os estagiários, ou programadores juniors que não dão atenção a isso. É comum encontrar aplicações onde o Projeto & Design não detalham como será o tratamento de exceção e, tampouco qual será a política para escrita dos logs. Com isso cada programador trata a sua maneira. 

E independente de existir orientações dos arquitetos sobre tratamentos de exceção e logs, existem erros comuns nestes tratamentos, os Antipatterns na manipulação de exceção e logs. 

Mas antes de falarmos sobre o que não se deve fazer, vamos comentar sobre o que se espera de um bom código, as boas práticas:  
Erros comuns encontrados em códigos Java, os Antipatterns:

segunda-feira, 21 de outubro de 2013

O que é o Code Review?

A revisão de código, inspeção estática de código, ou Code Review, é a verificação do código-fonte de um sistema de forma sistemática, mesmo que o sistema ainda esteja em desenvolvimento. O objetivo desta atividade é encontrar e arrumar erros antecipando ao máximo a fase inicial do ciclo de vida da aplicação, enquanto aumenta a qualidade do software e amadurece a habilidade do desenvolvedor.


A revisão pode ocorrer de algumas maneiras como:

  • Programação em Par - onde dois programadores utilizam a mesma estação de trabalho. Enquanto um programa, o outro passa algumas sugestões sobre como resolver o problema, enquanto verifica a qualidade como a solução está sendo implementada. O código já é construído sob a revisão de um par.
  • Análises Informais - quando a equipe troca código entre os desenvolvedores para realizar análises informais no código de outro desenvolvedor, ou a análise é feita por um profissional mais experiente dentro da equipe (como um arquiteto de sistemas). Neste caso, quaisquer inconformidades são informadas diretamente ao autor do código-fonte, sem nenhuma documentação formal.
  • Análises Formais - onde existe uma atividade formal dentro do processo de desenvolvimento. A revisão do código-fonte é realizada por outro desenvolvedor dentro da equipe, ou por um profissional mais experiente (como a Análise Informal). De forma resumida, a diferença é que esta análise precisa acontecer dentro do processo e precisa ser documentada.
  • Ferramentas de Code Review - ferramentas procuram de maneira automática erros comumente encontrados no código-fonte
Existe um consenso comum equivocado, onde a aprovação do usuário é suficiente para garantir a qualidade do projeto. A figura abaixo tenta ilustrar que isso não é bem verdade. Ainda mais que o usuário irá executar o fluxo básico de alguns casos de uso e não fará um teste exaustivo no sistema. Sem contar que itens como manutenibilidade e confiabilidade não são facilmente identificados com testes funcionais. 

Esta imagem exemplifica o porquê é errado achar que a validação do usuário é suficiente para garantir a qualidade do código-fonte

O que é Antipatterns?

Antipadrões de projeto de software, ou simplesmente antipatterns, é o termo utilizado para erros ou práticas comuns durante o processo de desenvolvimento de software. Este erro pode ocorrer durante diversas fases: análise, projeto, codificação, gerência, etc.


A inspiração deste conceito, surgiu em 1995 por Andrew Koenig no livro Gang of Four Design Pattern. Esse livro define alguns padrões para resolver problemas comuns para o desenvolvimento de software, atribuindo nomes para cada um destes padrões.

Três anos depois, surgiu o conceito de antipattern com o livro AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis. De maneira análoga, esse livro atribuiu nomes para padrões comumente encontrados nos códigos-fontes, entretanto que não são considerados como Boas Práticas.

Neste blog irei publicar diferentes tipos de Antipatterns, focando nos que mais impactam o desenvolvimento. 

sexta-feira, 18 de outubro de 2013

Bem Vindos ao Blog EasyALM!


Iremos abordar assuntos relevantes de Projetos, Processos, Testes, Requisitos, Negócios, Programação, Ferramentas e muito mais.


Acessem nosso blog diariamente para tirar suas dúvidas, partilhar de conhecimento e acompanhar nossas resenhas e casos de estudo sobre os assuntos citados acima.

Fiquem à vontade para acrescentarem idéias e dicas nos comentários para que possamos aplicar em nosso Blog!

Bons estudos e um grande abraço da EQUIPE ALMEASY!!!!!!!!


quinta-feira, 17 de outubro de 2013

Como elaborar um documento de Especificação de Requisitos

A seguir, um exemplo de como elaborar um documento de especificação de requisitos.

  1. Cabeçalho
    1. Usar o cabeçalho padrão adotado pela empresa
  2. Capa
    1. Indicamos que trata-se de um documento de requisitos, na linha abaixo citamos o nome do sistema e em outra linha a versão. Exemplo:
Documento de Requisitos do Sistema
EasyALM
Versão 4.0
  1. Histórico de Alterações
    1. Montamos uma pequena tabela onde é solicitado Data, Versão, Descrição e Autor.
  2. Conteúdo
    1. Criar um índice
  3. Introdução
    1. Iniciamos com uma breve descrição sobre o escopo do documento
    2. Visão geral do documento
                                          i.    Separando por seções, definimos como estão organizadas as informações. Exemplo:
1.     Seção 2 – Descrição geral do sistema
2.     Seção 3 – Requisitos Funcionais (RF)
3.     Seção 4 – Requisitos Não Funcionais (RNF)
4.     Seção 5 – Requisitos Negativos
5.     Seção 6 – Referências
    1. Convenções, termos e abreviações
                                          i.    Identificações dos requisitos
1.     Define-se termos e/ou nomenclaturas para os requisitos
                                         ii.    Prioridades dos requisitos
1.     Exemplo: Essencial, Importante e Desejável.
  1. Descrição Geral do Sistema
  2. Requisitos Funcionais
  3. Requisitos Não Funcionais
  4. Requisitos Negativos
  5. Referencias

Clique aqui para baixar um modelo exemplo

quarta-feira, 16 de outubro de 2013

O que é um Caso de Uso?

Um Caso de Uso é uma das principais técnicas para capturar requisitos (para novos sistemas ou manutenção). Cada caso de uso provê um ou mais cenários que indicam como o sistema deve interagir com o usuário final ou outro sistema para atingir um objetivo de negócio específico.

Casos de uso tipicamente evitam o jargão técnico, preferindo ao invés disto a linguagem do usuário final. Casos de usos são ferramentas enganosamente simples para descrever o comportamento de um software. Um caso de uso deve conter uma descrição textual de todos os passos que o usuário futuramente poderá vir a utilizar no software através de sua interface. 

Casos de uso não descrevem nenhum comportamento interno do software, nem fazem explanações de como o software será implementado, basicamente mostra os passos que o usuário deve seguir para usar o software. Recomenda-se que criar casos de uso para todas as formas de interação que o usuário terá com o software.

Exemplo simples dos casos de uso "Sacar Dinheiro" e "Imprimir Extrato" representados em diagrama UML:


Abaixo, um exemplo do caso de uso "Sacar Dinheiro" representado de forma escrita (o nível de detalhamento é opcional, o importante é clareza que os passos são descritos):

Caso de Uso: Sacar Dinheiro
Ator: Cliente
Escopo: Caixa Eletrônico
Pré-condição: Cliente autenticado para utilizar sua conta bancária
  1. Cliente escolhe a opção "Saque"
  2. Cliente informa o valor que deseja sacar
  3. Cliente confirma informações de saque
  4. Cliente retira o dinheiro do caixa e finaliza a operação.
-------------------------------------------------------------------------------------------------------
Outro exemplo simples de caso de uso representado por UML:




Mais informações sobre casos de uso:
  • Casos de Uso com Fluxo Alternativo
  • Casos de Uso com Fluxo de Exceção
  • Documento de Especificação de Casos de Uso