terça-feira, 14 de setembro de 2010

Cache pra que te quero?

Essa semana ocorreu a qcon aqui em São Paulo. O evento foi muito bom, embora algumas palestras recomendo menos teatro e piadas e mais objetividade no assunto.

De qualquer forma procurei ir em todas apresentações que falavam de escalabilidade, e um ponto me chamou atenção, um tema presente em praticamente todas apresentações e que curiosamente já estava preparando um post aqui no blog. É a utilização de cache.

Muitas vezes eu vejo pessoas falando em cache e na maioria das vezes vinculando cache apenas a dados armazenados. Quando envolve banco de dados, os dados armazenados estão em tabelas, então cacheiam as linhas da banco de dados. Bem, não que isso esteja errado, geralmente um banco de dados é remoto a aplicação e se o cache for local, obviamente a aplicação vai ficar mais rápida. Mas eu acho que tem que pensar em cache não para cachear apenas dados armazenados e sim para cachear dados processados.

Logo na primeira apresentação da qcon isso ficou bem claro, nela Nick Kallen descreveu a arquitetura de dados no twitter.

Você deve imaginar que o twitter tem uma base gigantesca de dados, e que não é um banco de dados relacional comum, eles usam muito estratégias de particionamento. Porém mesmo assim a tarefa de mostrar os tweets de uma pessoa é muito difícil. Quando você acessa o twitter do seu colega você vê os últimos tweets dele junto com os últimos tweets dos usuários que ele segue. Essa lista de tweets eles chamam de timeline.

Isso não é muito simples a medida que seu colega segue várias outros usuários e que esses usuários tem vários tweets postados. Imagina se para cada visualização de página de um usuário o twitter tivesse que buscar do banco e calcular o timeline?

Para isso eles usam cache, mas veja bem, não adianta cachear um tweet, ou os tweets de cada usuário, pois dessa forma eles estariam apenas mudando o ponto onde iria buscar o dado, ao invés do disco estariam buscando na memoria em um cache. Ok, isso iria melhorar a performance, mas não elimina a necessidade de montar o timeline, o que implicaria em buscar também no cache os últimos tweets de cada usuário que seu colega segue.

O que o twitter faz é cachear o timeline, ou seja, os tweets em uma ordem já definida para cada usuário. Eles não cacheiam apenas os dados da forma como estão armazenados no modelo, ele cacheia dados processados, o timeline já calculado, pois se cacheasse somente tweets, eles teriam o trabalho imenso de processar e montar o timeline para cada usuário a cada request.

Mas como assim? para todos usuários? Isso mesmo, o timeline esta calculado e processado em um cache para todos usuários do twitter. O cache é imenso, mas compensa.

Mas se um usuário postar qualquer coisa, o timeline fica invalido? Em teoria sim, a menos que ao postar algo, o twitter atualize todos timelines dos usuários que seguem o usuário que postou o tweet. Algo como, usuário José e João seguem o usuário Luiz, quando usuário Luiz posta nova mensagem, o timeline em cache do usuário José e João são atualizados. E adivinha? é isso que o twitter faz, porém um usuário pode ter milhares de seguidores, e o processo de atualização do timeline de todos eles pode demorar. Por isso a atualização é feita de forma assíncrona, e por esse motivo que um post no twitter não aparece imediatamente no twitter dos seus seguidores (Luiz posta e em um momento do futuro esse post vai aparecer no timeline de João e José).

Mas e se o cache morrer? Nesse caso o twitter vai penar bastante, afinal buscar os tweets para montar o timeline a partir do base pode ser extremamente custoso, teria que buscar e processar os últimos tweets de todos os usuários que determinado usuário segue. De qualquer forma há como, só é mais complicado e demorado. Ou seja, o twitter usa cache, mas se der qualquer problema, os dados que ali estavam podem ser totalmente reconstruídos através de informações da base de dados.

Enfim, o que realmente me chamou atenção nisso foi o exemplo claro de uma abordagem de cache que não apenas cacheia uma entidade do banco de dados, um registro de tabela, o resultado de uma query, o dado como ele está armazenado. Eles economizam não apenas queries mas também processamento, o processamento que seria necessário para criar o timeline, isso está lá cacheado também e é dessa forma que eu acho que deve ser pensado os caches.

Para não ficar apenas com o twitter, um outro exemplo. Imagine um sistema como paypal ou pagseguro, onde um usuário é redirecionado para realizar o pagamento de uma compra. Nesses sistemas geralmente quando o usuário comprador vai efetuar o pagamento, a página de compra é customizada para a loja do vendedor, o que implica buscar os dados das várias configurações dele, como customizações de tela, meios de pagamentos aceitos, limites, nome, telefone, etc.

Um sistema sem cache iria realizar várias queries direto no banco para buscar todos os dados da loja do usuário que está vendendo.

Um sistema com cache não muito bem pensado iria cachear 'n' registros de 'n' queries no banco, isso iria resultar em 'n' buscas no cache, objetos pequenos que representavam o resultado de cada query e o processamento de iteração entre esses objetos precisaria ser feito sempre.

Finalmente uma abordagem mais inteligente iria cachear um objeto maior, que não existe na base de dados e que iria conter todas informações passiveis de serem cacheadas já processadas e prontas para serem usadas. Então 'n' consultas no banco iriam resultar em uma única busca no cache de um objeto já pronto para ser usado, economizando não apenas em buscas, mas no processamento que iria ser realizado.

Bem é isso, cache é uma das soluções mais eficientes para resolver problemas de escalabilidade, isso ficou bem claro nas palestras que fui. Mas ao mesmo tempo, para se tirar melhor proveito dele, deve se pensar no que está cacheado, nem sempre colocar o cache junto aos DAO's do seu sistema pode ser a melhor alternativa.

domingo, 21 de junho de 2009

Business, Service e Generic Exceptions

Depois de um período de mudanças (sai de Uberlândia e vim morar em São Paulo), volto a postar. Vou falar sobre minhas impressões sobre os famosos BusinessException, ServiceException, GerericException, enfim, as exceções genéricas presentes em muitos sistemas por ai.

Imagine a arquitetura de uma aplicação web simples, onde temos os controladores acessando interfaces de serviço, até ai tudo bem. Agora imagina que uma dessas interfaces possui um método para incluir usuário, esse método poderia ter tal assinatura:

Usuario insereUsuario(String nome, String email, Date dataNascimento) throws ServiceException;

Parece normal, como muitos sistemas por ai, mas tem um grande problema, aquela exceção. Imagine que um esse método lance exceção quando:

  1. Encontre um usuário com mesmo nome já cadastrado.
  2. Encontre um usuário com mesmo email já cadastrado.
  3. O usuário tem menos de 18 anos.

Em cada uma das exceções a implementação lançaria um ServiceException:

throw new ServiceException("Já existe um usuário com esse nome cadastrado")

throw new ServiceException("Já existe um usuário com esse email cadastrado")

throw new ServiceException("O usuário deve ter pelo menos 18 anos")

Parece perfeito, no controlador eu faria:

Servico servico = ...
try {
    servico.insereUsuario(nome, email, dataNascimento);
} catch(ServiceException e) {
    erros.add(e.getMessage());
}

Economia de linha no try catch, sem varias exceções, a mensagem vem do domínio e eu apenas jogo na tela para o usuário ver.

Mas ai vem dois grandes problemas, o primeiro é encapsulamento e o segundo a internacionalização. Eu, como controlador web ou como qualquer outro cliente da interface de serviço, quero direcionar o usuário da minha aplicação web para meu site de adolescentes caso o usuário não possa ser incluído devido a problemas de idade. Como faria isso? duas formas:

A primeira é antes de chamar o método de serviço testar se ele tem mais de 18 anos, o que resultaria em quebra de encapsulamento da minha lógica de serviço, se no futuro meu serviço mudar e eu aceitar usuários a partir de 16 anos, esse controlador (cliente do meu serviço) precisará ser alterado.

A segunda solução seria testar a mensagem de erro que a exceção de serviço (ServiceException) retornou (arrhhgg), caso for a mensagem de idade invalida, eu faço o direcionamento, o que também seria uma quebra de encapsulamento. Notem que não tem como eu cliente realizar essa lógica minha sem conhecer detalhes da implementação, isso porque nossa interface tem aquele ServiceException genérico, ela não nos diz muita coisa sobre as exceções que podem lançar, caso eu não queira simplesmente mostrar a mensagem para o usuário da minha aplicação web e sim fazer tratamentos diferentes para as varias exceções que modelo pode lançar.

Para quem realiza testes unitários de suas classes, nunca notou algo errado quando precisa fazer um teste para testar uma exceção especifica? Como faz, assim?

@Test
public void testaInclusaoDeUsuarioComNomeJaExistente() {
  try {
    service.incluiUsuario("nomeExistente", "email@teste.com", new Date());
  } catch(ServiceException e) {
    assertEquals(e.getMessage(), "Já existe um usuário com esse nome cadastrado")
  }
}

Perceba a baita quebra de encapsulamento, não é assim que se testa exceções, ou vocês acham que aquelas anotações que alguns frameworks de teste tem (como o springtest) @ExpectedException não servem para nada?! Usando exceções genéricas, elas realmente não servem para nada, pois não tem como usar de forma confiável, pois a exceção pode vir indicando que ocorreu X quando na verdade você queria testar a ocorrência da exceção indicando Y.

O outro problema que comentei foi internacionalização. O chefe vem e fala: vamos vender esse sistema para fora do Brasil, teremos que ter um sistema internacionalizavel. Você pensa: "beleza, coloca um buddle com as mensagens em cada idioma, e mudo todas exceções para ao invés de passar a string da mensagem, passar um código, as exceções retornam o código e meu framework web joga na tela a mensagem associada a esse código".

Mais uma vez quebra de encapsulamento, o que era antes isso:

@Test
public void testaInclusaoDeUsuarioComNomeJaExistente() {
  try {
    service.incluiUsuario("nomeExistente", "email@teste.com", new Date());
  } catch(ServiceException e) {
    assertEquals(e.getMessage(), "Já existe um usuário com esse nome cadastrado")
  }
}

Passou a ser isso:

@Test
public void testaInclusaoDeUsuarioComNomeJaExistente() {
  try {
    service.incluiUsuario("nomeExistente", "email@teste.com", new Date());
  } catch(ServiceException e) {
    assertEquals(e.getMessage(), "nome.do.usuario.ja.existe")
  }
}

Pense nisso, seu serviço provavelmente é stateless, você não sabe qual a localidade do usuário, então não tem como mandar uma mensagem bonita, já formatada para o idioma, pois você não sabe o idioma do seu usuário (a menos que o locale seja passado em todos seus métodos de serviço - nunca pense nisso), tem que mandar esse código mesmo, quem faz a tradução é quem está ali próximo do usuário, conhece ele, ou seja, a camada web, de interface com o usuário, mas não fica estranho?! Os códigos de erros definidos em uma camada de domínio e as mensagens definidas na camada web?!

Mas enfim, minha questão é, em meio de tempos de debates sobre VO, DTO e modelos anêmicos, porque as pessoas têm preguiça de criar exceções e gostam tanto de usar uma exceção genérica? Mesmo com esses problemas já mostrados?!

Como eu acho que deve ser feito: a inclusão de usuário recebe 3 parâmetros, nome, email e data de nascimento, o que pode ocorrer? Não quero dois usuários com mesmo nome nem mesmo email e também não quero usuários com idade menor que 18 anos. Isso é da interface, a minha interface de serviço tem que dizer isso, portanto a assinatura do meu método seria essa:

Usuario insereUsuario(String nome, String email, Date dataNascimento) throws UsuarioJaExistente, IdadeInvalidaException;

Podendo haver uma hierarquia de exceções, como por exemplo:

public class RegistroJaExistenteException extends Exception

public class UsuarioJaExistenteException extends RegistroJaExistenteException

public class NomeUsuarioJaExistenteException extends UsuarioJaExistenteException

public class EmailUsuarioJaExistenteException extends UsuarioJaExistenteException

Agora eu como cliente desse serviço posso fazer uns try catch e tratar as exceções de forma normal, minhas "mensagens" de erro agora estão na assinatura do método, se vier um IdadeInvalidaException eu faço alguma coisa especifica, posso redirecionar para outro site ou posso simplesmente chamar a minha mensagem “usuario.com.idade.invalida" na locale do usuário.

Meus testes unitários ficariam bonitos:

@Test
@ExpectedException(NomeUsuarioJaExistenteException.class)
public void testaInclusaoDeUsuarioComNomeJaExistente() {
  service.incluiUsuario("nomeExistente", "email@teste.com", new Date());
}

Como pode ver, dessa forma, você ira criar varias exceções, para gerenciar todas elas, com certeza você também não criara os packages da sua aplicação no estilo:

br.com.empresa.projeto.dao
 -- todos daos
br.com.empresa.projeto.entity
 -- todos entities
br.com.empresa.projeto.service
 -- todos servicos
br.com.empresa.projeto.exception
 -- todas exceções

Vai à sugestão de criar algo no estilo:

br.com.empresa.projeto.usuario
 Usuario
 UsuarioService
 UsuarioRepository
 UsuarioDAO
 UsuarioJaExistenteException
 EmailUsuarioJaExistenteException
 NomeUsuarioJaExistenteException
 IdadeInvalidaException
 etc

Agrupando em packages o que são relativos ao negócio e não agrupando pelo "tipo" de objetos (DAO's, Entities, Service's, etc).

Caso você já esteja no meio do projeto, não é tarde demais, refatoração faz parte de praticas ágeis, mas você não precisa sair por ai alterando tudo de uma só vez. Você pode considerar sua ServiceException como java.lang.Exception, começar a criar umas classes filhas para os novos desenvolvimentos e aos poucos refatorar o que já esta feito, não vai quebrar o código do seu cliente ou seu controlador (quem usa sua classe de serviço), pois vai continuar lançando ServiceException, com a vantagem que suas interfaces vão ficando aos poucos com uma assinatura mais descente.

Bem, eu já criei sistemas com BusinessException, aprendi com o erro, hoje é muito claro para mim que esse modelo não é certo. Utilizar serviços com exceções genéricas é terrível (a interface não te diz nada sobre o que pode ocorrer), fazer internacionalização fica muito chato, será que a criação de uma classe a mais indicando a exceção é tão custosa assim?! Eu acho que não.

segunda-feira, 2 de março de 2009

Como não criar um framework

Muitas pessoas tem a bendita ideia de pegar seus códigos fontes, a maneira como elas desenvolvem, seus padrões e criar um framework, uma arquitetura de referência para impor na empresa e todos outros desenvolvedores usarem. Geralmente esses frameworks são baseados em outros frameworks que irão evoluir e com certeza o framework criado não vai conseguir evoluir junto, causando um puta atraso para a empresa e também para os desenvolvedores que são obrigados a usar a gambiarra.

Por outro lado existem pessoas que realmente fazem frameworks úteis, que não tem o objetivo de resolver todos os problemas do mundo, e sim um problema especifico e que ainda não tenha solução.

De qualquer forma, se você ainda tem a ideia de fazer uma arquitetura de referência para resolver a fome do mundo e definir todas as camadas de todos futuros sistemas da sua empresa, ou então tem uma ótima ideia para um framework realmente útil, vale a pena ler esse comentário feito pelo Rod Johnson, criador e CEO do Spring em entrevista para a revista Java Magazine deste mês:

Houve uma série de decisões de projeto / design que foram importantes para que o Spring prevalecesse, uma delas foi que muitos frameworks são baseados na idéia de restringir os desenvolvedores, acreditando que os desenvolvedores e projetistas do framework sabem mais que os usuários do framework. E por isso um dos objetivos do framework é checar e controlar o que os desenvolvedores fazem.

Tivemos uma abordagem diferente com Spring, acreditando que desenvolvedores são profissionais que merecem ser tratados com respeito, ao invés de usar rótulos e forçá-los a trabalhar da forma que queremos que eles trabalhem.

Confesso que conheço muitos desenvolvedores que merecem ser presos e controlados. Nesse comentário, a frase que mais me chamou atenção foi: desenvolvedores são profissionais que merecem ser tratados com respeito. Esse pensamento já vem a algum tempo martelando minha cabeça. Hoje sou muito a favor de tratar desenvolvedores como profissionais, não precisamos e nem devemos fazer frameworks para controlar as coisas que esses caras fazem no sistema, afinal, ele é um profissional, recebe por isso e deve agir e ser cobrado como tal. Caso ele não tenha atitude nem conhecimento necessário para o desenvolvimento no projeto, na minha opinião ele deve ir para outro projeto, para não ser radical e dizer que ele não é necessário para empresa. As reuniões de retrospective e diária do scrum são ótimas para identificar esses caras ;).

Enfim, se for criar um framework, crie algo no espírito do Spring, tenha em mente como alvo desenvolvedores profissionais. Tendo essa mentalidade, caso sua ideia é fazer uma arquitetura de referência, você provavelmente já desistiu. O segundo apelo é para os responsáveis pela contratação de desenvolvedores, por favor, contratem bons profissionais e ajude as arquiteturas de referência a morrerem de vez.

quinta-feira, 16 de outubro de 2008

Entendendo o padrão de projeto Adapter

Então, faz muito tempo que não posto neste blog, mas agora estou com internet de novo em casa, posso tentar atualizar meu blog com mais freqüência. Vou postar outra coisinha simples, mas que muita gente não conhece, relacionado a padrões de projeto (design patterns), um assunto que eu considero o mais, senão um dos mais importantes para alguém que trabalha com orientação a objetos.

Um padrão de projeto é uma solução comum para um determinado problema, existem 23 padrões "clássicos" para programar orientado a objetos, eles são conhecidos como os 23 padrões GoF (Gang of Four devido as 4 pessoas que criaram eles), além desses 23 padrões clássicos existem ainda padrões J2EE (recomendados pela SUN), padrões GRASP (General Responsibility Assignment Software Patterns) entre outros. Qualquer um pode "criar" um padrão, eles surgem de acordo com a evolução das arquiteturas e do desenvolvimento dos sistemas.

Hoje vou falar de um padrão do GoF que eu costumo usar conhecido como Adapter, esse padrão tem como objetivo converter uma interface de uma classe em outra interface esperada pelos clientes, exemplos da utilização pode ser visto na API padrão do java para tratamento de eventos (MouseAdapter, WindowAdapter), Wrappers (Integer, Double), etc. Existem duas formas de Adapter: Class Adapter que utiliza herança múltipla e Object Adapter que utiliza composição. Class Adapter é usado quando o cliente alvo que eu quero adaptar possui uma interface estatica, Object Adapter é usado quando se deseja menor acoplamento ou quando o cliente alvo que quero implementar não usa interface.

Mas vamos aos códigos. Imagine que estamos utilizando um framework de cinema, que possui uma classe para representar filme, uma interface que define ator, e uma classe que representa o ator principal (só para existir uma implementação de ator). Em um filme eu posso incluir vários atores e então rodar o filme:

public interface Ator {

 void representar();

}

public class Filme {

 private List<Ator> atores = new ArrayList<Ator>();

 public void addAtor(Ator ator) {
  atores.add(ator);
 }

 public List<Ator> getAtores() {
  return Collections.unmodifiableList(atores);
 }

 public void rodaFilme() {
  for (Ator ator : atores) {
   ator.representar();
  }
 }
}

public class AtorPrincipal implements Ator {

 public void representar() {
  System.out.println("Ator principal representando");
 }

}

Essas são classes e interfaces do framework de cinema, eu não posso alterar. Agora imagine que eu tenho meu sistema que possui pessoas, e um comportamento dessas pessoas é andar:

public class Pessoa {

 public void andar() {
  System.out.println("Pessoa andando");
 }

}

Com essa minha classe de pessoa, eu queria incluir figurantes no meu filme, pessoas que não são atores, só para ficar andando lá, fazendo número no filme, porém não posso alterar a interface do filme para receber pessoas, o que eu faço?! Adapto pessoa para ser ator, dessa forma:

public class Figurante1 extends Pessoa implements Ator {

 public void representar() {
  andar();
 }

}

Nesse caso eu usei Class Adapter, pois adaptei a classe pessoa a um ator. Porem esse adaptador não serve para qualquer pessoa, ele é uma nova Pessoa, nova classe, ele não adapta pessoas já existentes a um ator, eu tenho que criar realmente um figurante novo, não adaptar uma pessoa, por isso Class Adapter. Caso você precisasse adaptar qualquer pessoa a um figurante, essa não seria a melhor forma, o ideal é que a classe figurante (adaptadora) fosse dessa forma (usando object adapter):

public class Figurante2 implements Ator {

 private Pessoa pessoa;

 public Figurante(Pessoa pessoa) {
  this.pessoa = pessoa;
 }

 public void representar() {
  pessoa.andar();
 }

}

Ambos são adaptadores, qual usar? Depende do que você quer, em minha opinião, o perfeito é dar as duas opções, eu adaptar uma pessoa já existente ou eu criar uma nova pessoa que já é um figurante, para isso pessoa deveria ter uma interface, e ai meu figurante seria dessa forma:

public class Figurante3 implements Ator, InterfaceDePessoa {

 private InterfaceDePessoa pessoa;

 public Figurante(InterfaceDePessoa pessoa) {
  this.pessoa = pessoa;
 }

 public Figurante() {
  this.pessoa = new Pessoa();
 }

 public void representar() {
  pessoa.andar();
 }

 public void andar() {
  pessoa.andar();
 }

}

Isso me permite adaptar qualquer pessoa a um figurante, bem como criar um novo figurante do "zero", ou seja, é uma junção dos dois figurantes apresentados anteriormente. Mas, meu sistema não tem uma interface de Pessoa e eu não quis criar, então vou usar meus dois primeiros figurantes:

public class Main {

 public static void main(String[] args) {
  Ator a1 = new AtorPrincipal();  //um ator de verdade
  Figurante f1 = new Figurante1();  //uma nova pessoa que ja nasceu Figurante
  Pessoa p1 = new Pessoa();  //uma pessoa normal
  Figurante f2 = new Figurante2(p1);  //a pessoa normal adaptada a figurante

  Filme filme = new Filme();
  filme.addAtor(a1);
  filme.addAtor(f1);
  filme.addAtor(f2);

  filme.rodaFilme();
 }

}

Agora vamos evoluir, achei um framework que gerencia tarefas, e eu quero incluir tarefas que ao serem executadas fazem com que todos os atores do filme representem. Esse framework tem uma classe abstrata que representa uma tarefa e um gerente de tarefas:

public abstract class Tarefa {

 public abstract void executar();

}

public class GerenteTarefa {

 public void executar(List<Tarefa> tarefas) {
  for (Tarefa tarefa : tarefas) {
   tarefa.executar();
  }
 }

}

Mas repare que não tenho uma interface de tarefa, então como vou adaptar atores (qualquer ator, até mesmo o figurante) a tarefas?! Solução: Object adapter, ou seja, vou criar meu adaptador a partir de objetos com composição (mesma idéia do figurante2, pois eu quero adaptar qualquer ator a tarefas):

public class AdaptadorDeAtor extends Tarefa {

 private Ator ator;

 public AdaptadorDeAtor(Ator ator) {
  this.ator = ator;
 }

 public void executar() {
  ator.representar();
 }

}

Repare que eu não adaptei a classe, meu adaptador não é um novo ator, ele é uma tarefa que delega seu método “executar” para um objeto de ator, ele adapta qualquer ator a tarefa, executando isso:

public class Main {

 public static void main(String[] args) {
  Ator a1 = new AtorPrincipal();  //um ator de verdade
  Figurante f1 = new Figurante1();  //uma nova pessoa que ja nasceu Figurante
  Pessoa p1 = new Pessoa();  //uma pessoa normal
  Figurante f2 = new Figurante2(p1);  //a pessoa normal adaptada a figurante

  Filme filme = new Filme();
  filme.addAtor(a1);
  filme.addAtor(f1);
  filme.addAtor(f2);

  filme.rodaFilme();

  List<Tarefa> tarefas = new ArrayList<Tarefa>();
  for (Ator ator : filme.getAtores()) {
   tarefas.add(new AdaptadorDeAtor(ator));
  }

  GerenteTarefa gt = new GerenteTarefa();
  gt.executar(tarefas);
 }

}

Ou seja, consegui adaptar minha classe Pessoa para um ator e depois adaptei um ator para tarefa, usando Class e Object Adapter. Entendendo isso, você consegue adaptar qualquer classe para praticamente qualquer interface, talvez muita gente já fez isso, mas não sabia que tinha nome. Para quem nunca usou, pense bem, pode ser muito útil na reutilização de código, integrar sistemas e pode ajudar bastante a melhorar o desenvolvimento.

segunda-feira, 26 de maio de 2008

Scrum, meu novo interesse

Semana passada eu fiz um curso de scrum na caelum, eu não tenho tempo nem conheço tanto para explicar como funciona scrum em geral, mas as impressões que fiquei foram muito, mas muito boas mesmo.

Parece uma metodologia que, ao mesma tempo da muita liberdade para o time (desenvolvedores), mas é cruel com pessoas que não rendem, os espertinhos, “enroladores”, pois a cobrança não vem de cima, vem dos lados, dos próprios companheiros de time. A metodologia é baseada muito na confiança da equipe, é uma confiança controlada através de reuniões diárias, mas que faz com que o desenvolvedor se sinta bem desenvolvendo. O scrum me parece realista para a maioria dos problemas de desenvolvimento de sistemas e alguns sociais que podem existir em uma equipe, nele você sabe que o cliente vai mudar os requisitos, você sabe que o programador não vai cumprir exatamente o tanto de horas que ele orçou, trabalha muito com feedback, sinceridade entre a equipe e o mais fantástico, a própria metodologia oferece recursos que ajudam a trabalhar e melhorar isso, como as reuniões diárias e retrospectiva, tudo visando o bem da equipe e não apenas da pessoa. Acho que o sentimento de alguém que trabalha dessa forma é: “eu prefiro ser o goleiro do time vencedor do campeonato, do que um excelente goleiro de um time rebaixado”.

Bom, estamos tentando adotar scrum aqui na empresa (já eheh), o que o Alexandre (nosso instrutor) disse na aula parece que aconteceu, santo de casa não faz milagres, embora agente explique e as pessoas estudem a metodologia, elas duvidam que funcione, por mais que você fala, elas duvidam e querem até mesmo mudar a forma como scrum é, impondo mais responsabilidades de controle da equipe ao scrum master, mudando o planning poker, mudando a reunião de planejamento, enfim, alterando a forma como scrum trabalha, inserindo itens que acabam com toda a vantagem que a metodologia oferece. Mas, eu estou bem confiante, consegui passar a importância de se seguir a metodologia, o porque que é assim, e no fim, acho que vamos seguir o planejado e o certo, espero dar tudo certo e provar que realmente funciona.

Sobre o curso eu aconselho todos que façam porque realmente é bom, scrum é uma metodologia simples, com técnicas bem sociais e realistas, mas que se for pensar bem, é ideal para desenvolvimento de sistemas.

Ai a foto da galera que participou do treinamento (roubada do blog do Alexandre):