March 22nd, 2009 — Dicas
“Todo novo projeto (emprego, hobby ou empresa)” começa de maneira excitante e divertida. Depois, fica mais difícil menos divertido, até que atinge seu ponto mais baixo: extremamente difícil e nada divertido. Então você se pergunta se todo aquele sofrimento vale a pena.”
Com essa frase, começa o livro “O Melhor do Mundo - Saiba quando insistir e quando desistir”, de Seth Godin (ISBN 9788575424476, ou, no nome em inglês The Dip: A Little Book That Teaches You When to Quit and When to Stick, ISBN 9781591841661). De acordo com o autor, o que diferencia uma pessoa extremamente bem-sucedida da maioria é a habilidade de escapar o mais rápido possível das situações sem futuro e de se manter concentrada e motivada quando realmente importa.
De uma maneira geral, esse é aquele tipo de livro que uma pessoa somente irá ler quando se encontrar na situação citada pela premissa do mesmo - o que, no caso deste assunto em específico, provavelmente já deve ter ocorrido com todos nós pelo menos uma vez (comigo, uma centena de vezes). Cheguei ao livro após indicação de um amigo, referente a uma mensagem que eu havia postado no Twitter. Tudo bem, a mensagem do Twitter falava sobre Business Plan, mas mesmo assim o livro veio em uma ótima hora.
O quê de mais interessante tem “O Melhor do Mundo” a oferecer? Falando por mim, foi o fato de descobrir que existe uma determinada série de eventos conhecidos e documentados (como a desmotivação em determiando trabalho após um tempo nele) e que, realmente, quando iniciamos algo novo, como um emprego ou a mais nova “idéia gênio para ficar rico vendo grama crescer”, existe o pico de alta motivação inicial e a posterior queda, a qual se estende por um variável período de tempo (o chamado “Vão” - ou Dip - pelo autor), que, se superado leva ao objetivo final (carreira bem sucedida e feliz / idéia que deu certo e muito dinheiro).
Seguindo essa premissa, logo nas primeiras páginas (é um livro curto, com não mais de 90 páginas), Seth Godin fala e de lugares que a tragetória pode levar, como a um Beco sem Saída ou a um Abismo, e dá dicas de o que levar em consideração na hora de decidir se devemos continuar ou se devemos desistir. O Beco sem Saída, como o próprio nome diz, é aquilo que não te levará a lugar algum (um emprego sem o menor futuro, uma idéia implementada de maneira errada que você não quer largar por motivos diversos), enquanto que o Abismo é uma situação que “não se pode desistir” até que tudo caia, e ai a dor é grande demais para suportar.
O Beco sem Saída e o Abismo são as curvas que levam ao fracasso, enquanto o Vão é a curva que, se superada, lhe gratifica no final com o sucesso ou objetivo esperados inicialmente. O Vão é a curva que separa o jogar casual de tênis do profissional; é aquela diferença entre ser “apenas bom” e ser “excelente”. É a experiência que somente vem com anos de prática e vivência.
A mensagem do livro pode ser facilmente entendida logo no início, e depois ele se repete bastante, explicando as mesmas coisas com palavras e exemplos diferentes, sem muito mais a acrescentar. Após a definição dos termos e problema (o que é rapidamente compreendido), o autor passa a descrever alguns exemplos de situações, deixando claro que desistir não significa fracassar, e que pode ser a chave para largar uma idéia sem futuro para recomeçar outra que lhe dará o sucesso esperado.
Esse ponto, o de “Desistir”, é tratado repetidamente durante todo o livro, e o autor têm uma visível preocupação em deixar claro que pessoas e empresas vencedoras desistem frequentemente, e que se não fizessem isso, estariam fadadas ao fracasso, apegadas a idéias e projetos sem futuro. Não há nenhuma fórmula ou regra sobre como tomar tal decisão, apenas um conjunto de dicas que podem lhe ajudar em tal.
Como disse acima, a melhor parte do livro está logo no início, e ele lhe será útil caso você se encontre em uma situação que não sabe se segue em frente ou se larga tudo e parte para a próxima. Quem estiver bastante satisfeito com o trabalho ou afins não tem muito o que aproveitar caso não o leia com a mente bastante aberta.
Algumas citações do livro:
- A maioria das pessoas desiste, elas só não fazem isso direito.
- Você não pode tentar fazer tudo, especialmente se tentar ser o melhor do mundo em sua área.
- Sem muito tempo para experimentar, nós intencionalmente reduzimos nossas escolhas ao que consideramos melhor.
- O conceito de “Melhor do Mundo” para mim pode ser diferente do seu conceito de “Melhor do Mundo”. “Mundo” é um termo bastante flexível, subjetivo.
- Em face de tanta variedade, as pessoas entram em pânico. (ps: isso é abordado pelo “Paradoxo da Escolha”, ou “Paradox of Choice”)
- Se bastasse não desistir para ser bem sucedido, por que pessoas menos motivadas e talentosas que você venceriam?
- A desistência estratégica é segredo das organizações bem-sucedidas.
- Logo que você começa alguma atividade, tudo parece divertido.
- O Vão é a longa e cansativa caminhada entre o início e a maestria.
- O Vão é o que distingue a sorte de principiante de uma realização real.
- O maior obstáculo para o sucesso na vida [....] é nossa inabilidade de fugir dessas curvas (beco sem saída e abismo) o mais rapidamente possível.
- A área de vendas seria um paraíso caso não existisse o risco constante de ninguém querer comprar seu produto.
- O essencial é saber que o Vão está lá. Ter consciência de que você está enfrentando esse tipo de desafio é o primeiro passo para superá-lo.
- Desistir no momento certo é difícil. A maioria das pessoas não tem coragem para isso.
- Não basta sobreviver ao Vão. É preciso usá-lo como uma oportunidade de criar algo extraordinário que as pessoas não vão ter como deixar de comentar, recomendar e escolher.
- O oposto de desistir é se dedicar novamente.
January 16th, 2009 — Dicas
Me dei conta que nos últimos dias descobri uma série de produtos e / ou funcionalidades legais que realmente tenho tido a oportunidade de usar, e que com certeza serão úteis para várias outras pessoas. Em alguns casos foi uma melhoria para um software já existente, em outro um site que disponibilza certas coisas. A diferença disso - e a razão de colocar isso publicamente aqui - é que, diferentemente de muitas outras coisas que vemos e baixamos todos os dias, essa pequena lista que mostro logo abaixo eu realmente tenho utilizado e gostado bastante.
Eis uma rápida lista:
Ferramenta para backup - Mozy.com
Negligenciamos todo e qualquer bakcup até o dia em que perdemos dados. No meu caso não foi diferente. Com 6 meses de uso, o HD do meu MacBook foi pro espaço, a ponto da empresa de restauração dizer que ele estava totalmente inutilizável, devido a enorme quantidade de riscos existentes. Posteriormente fiquei sabendo que o crash havia sido causado devido a falhas no lote do HD que eu tinha. Junto com o HD, foram-se embora 7 anos de fotos (algo em torno de 12 mil fotos), umas 1500 músicas, sem contar em documentos extremamente importantes, como minha planilha de controle financeiro diário dos últimos 2 anos, contratos e documentos de projetos realizados, informações sobre alguns websites que mantenho, e assim por diante. Nada, nada divertido.
Por um lance de sorte intrigante, achei um HD do tempo que usava um PC Desktop com todos os dados até a data em que comecei a usar o Mac. Ou seja, consegui recuperar a maior parte dos arquivos perdidos. Mas isso, claro, nem Zahl explica.
Depois desse susto, resolvi criar vergonha na cara e passar a adotar a prática de realizar backups diários do meu computador. Comprei um HD externo, de notebook, que para funcionar basta ligar na porta USB, e habilitei a TimeMachine (programa que vem com Mac OS X para realizar backups incrementais diários de todo seu computador). Não obstante, por pura paranóia, procurei websites que permitissem realizar backup remoto dos arquivos.
Após avaliar vários serviços, conclui que os serviços disponibilizados pelo site Mozy.com eram os melhores. O Mozy é grátis para o uso de até 2GB de armazenamento (o que deve ser suficiente para a maior parte das pessoas, contando que músicas e afins não sejam tão importantes), e é perfeito para armazenar documentos e coisas afins. Para tornar as coisas mais fáceis, você pode fazer o download de um programinha que fica rodando em background em seu computador, e automaticamente realiza o backup diário e incremental de seus arquivos. Não precisa ficar se preocupando em lembrar de realizar a tarefa. Basta instalar, configurar e esquecer. Mais fácil, impossivel.
Caso você precise de mais de 2GB em armazenamento, o Mozy Pro fornece backup ilimitado pela merreca de 50 doláres por ano. Eu tenho esse plano, onde guardo 40GB em documentos, músicas e fotos.
O site é http://www.mozy.com
Conferência via Internet - Skype e Scriboard
Ok, todo mundo já conhece o Skype, mas a razão de comentar dele aqui é que, ao contrário das versões para Windows e Linux, a versão para Mac ainda não tinha a opção de compartilhar a tela (desktop, programas que estamos usando). Agora, a última versão (ainda em beta, na data de escrita deste post) para Mac OS X permite compartilhar todo o desktop com os participantes da conferência. Isso é excelente para fazer demonstração de produtos, pedir ajuda para amigos, mostrar ao cliente como o sistema está ficando, e assim por diante.
A opção para compartilhar a tela está disponível no menu Call -> Share Screen. O site do Skype é http://www.skype.com
A respeito do Scriboard, ele é um programinha para Mac que permite desenhar em cima das janelas. Isso mesmo: desenhar em cima de qualquer programa aberto. Quando utilzado junto com a funcionalidade de Screen Sharing do Skype, permite que as conferências fiquem muito mais interativas, pois ao invés de falar algo como “… vejam o link xpto no menu do canto superior direito, do lado do botão vermelho”, basta ir até o link e fazer um círculo ao redor dele. É realmente fantástico.
O site do Scriboard é http://www.macmax.org/rubrique.php3?id_rubrique=16&lang=en, e a licença custa US$ 20,00.
Gravar apresentações e demos - ScreenToaster
O meu delicious, na parte de “Screen” (seja sharing, record e afins) tem quase três dezenas de links, mas a melhor ferramenta que já vi até hoje, levando em conta praticidade de uso, custo, funcionalidades e qualidade, é o ScreenToaster. ScreenToaster permite que você grava, com som, toda a atividade que está sendo realizada no seu computador, sem precisar instalr qualquer coisa. É tudo via web, através de um programinha em Java. Os vídeos podem ser armazenados no próprio site do produto, além de permitir exportação para arquivos AVI e YouTube (em um futuro próximo).
O ScreenToaster foi uma das melhores descobertas que fiz nos últimos tempos. O site é http://www.screentoaster.com/, e para utlizar basta criar uma conta grátis.
December 28th, 2008 — Uncategorized
O Acordo Ortográfico da Língua Portuguesa foi assinado em Lisboa, em 16 de Dezembro de 1990 por diversos países falantes deste idoma, e aprovado no Brasil em 1995. Depois de quase duas décadas da assinatura, chegou a hora de colocar em prática. O dicionário Michaelis, da editora Melhoramentos, criou um documento em PDF explicando de forma clara e direta as mudanças, sendo uma excelente fonte de consulta.
O link para download é http://www.livrariamelhoramentos.com.br/Guia_Reforma_Ortografica_Melhoramentos.pdf
December 10th, 2008 — Dicas
Essa é para quem, assim como eu, sofre com o consumo de memória excessivo por parte do Firefox.
O meu firefox, último release estável da versão 3, facilmente chegava a casa dos 600 MB de ram utilizados, mesmo após ter fechado a maior parte das tabs. Durante meses, constatei que o consumo de memória apenas crescia, nunca - ou raramente - decrescendo. A situação ficava pior ainda quando assistia vídeos e outros tipos de streaming, quando o consumo aumentava consideravelmente.
Eu tenho o costume de ficar com literalmente dezenas de tabs abertas em certos momentos, onde muitas delas são relacionadas a coisas do trabalho, enquanto outras estão em webmail, ou bloglines, ou em inúmeras páginas de conteúdo, que vou lendo aos poucos ou abro para ler mais tarde. Claro, existem algumas extensions, como o Read it Later e GMail Manager (que permite acompanhar múltimas accounts do gmail) que ajudam a diminuir o número de tabs, mas a regra geral se aplica.
O fato é que o uso de memória sempre foi excessivo. O problema não é generalizado, pois constatei que outras máquinas com o mesmo Firefox não tinham um consumo tão elevado. Talvez a grande quantidade de plugins (19, atualmente) (ou seria algum em específico?) que uso causem o problema, mas como desinstalá-los não é uma opção, nos resta procurar algma outra solução. Pesquisando no Google sobre o “problema” (como “firefox memoy leak”) vi que isso não se deve necessariamente a algum vazamento de memória, tanto que os desenvolvedores chamam isso de “Funcionalidade”.
Eis a razão: o Firefox, em tentativas de tornar a navegação mais rápida, armazena em memória diversas coisas, como as “n” últimas páginas visitadas, realiza “pre-fetching” de páginas, e assim por diante. Isso tudo acarreta em um uso maior de memória. Por sorte, é bastante fácil mudar isso.
Todas os passoas a seguir devem ser feitos na página de configurações, que é acessível através da URL “about:config” - ou seja, abra uma tab nova no Firefox, e digite about:config. A tela abaixo deverá ser mostrada:

#1 browser.cache.memory.capacity
Controla quanto de memória é utlizado para cachear as páginas. No campo “Filter“, digite “browser.cache.memory.capacity“. Caso nada seja retornado, você deverá criar este valor. Para tanto, clique com o botão direito do mouse, e selecione New -> Integer. O campo “New Integer value”, digite “browser.cache.memory.capacity”, e no próximo diálogo informe o valor em KB do cache. Eu utilizei 1024 (1 MB).
Caso a chave já exista, mude-a para o valor que desejar.
#2: browser.cache.disk.capacity
Determina quanto de espaço em disco é utlizado para cachear as páginas. Defina para o valor que desejar. Eu utilizei 5000.
#3: config.trim_on_minimize
Esta opção, que parece funcionar apenas em Windows (utilzo Mac), informa ao Firefox para liberar memória quando for minimizado para a barra de tarefas. Para criar esta chave, caso não exista, clique com o botão direito e escolha New -> Boolean, definindo seu valor para “true”.
#4: network.prefetch-next
Quando definido para “true” (New -> Boolean), informa ao Firefox para fazer “pre fetching” (”busca em background”) de todos os links definidos com o atributo “rel=prefetch”. Eu deixei essa opção para “false”, pois não quero que páginas que muito provavelmente não acessarei fiquem consumindo minha banda e memória RAM.
#5: browser.sessionhistory.max_total_viewers
Controla quantas páginas ficarão no histórico dos botões “Voltar” (Back) e “Avançar” (Forward) do browser. Eu deixe essa opção (New -> Integer) para um valor baixo, como 5.
Venho testando essa configuração a vários dias, e posso afirmar que o consumo de memória caiu quase pela metade, e, principalmente, liberando memória quando chega em um determinado patamar. Ou seja, o Firefox realmente libera memória depois de um certo tempo.
December 1st, 2008 — Programação
Desenvolvimento de software se encaixa na área de ciências exatas, mas a tarefa em si está longe de ser algo exato. Aliás, muito longe disso. Programar sistemas, seja em qual for a linguagem, tem muito de numerologia, astrologia, tarô e outras mandingas populares. Sim, tem que ter, ou qual seria a razão para bugs ocorrerem? Falta de testes? Sim, claro, a falta de testes é um problema grave que muitos ainda tentam ignorar, mas mesmo em um software bem (?) testado bugs ocorrem - afinal, o teste certifica que o código escrito para alguma regra de negócio em particular de fato funciona, mas quem garante que a lógica utilizada está correta?
Pois é, esporadicamente acontece de nosso código estar de fato errado, e consequentemente o teste está certificando algo (a regra de negócio) que não é correto. Ossos do ofício.
No JForum 3, um bug recente foi bastante intrigante, peculiar. Apenas para situar e garantir que estamos todos no mesmo contexto, JForum é um sistema de fóruns de discussão, e fóruns contêm Tópicos, que contêm Posts. O bug em questão começou a ocorrer logo depois que adicionei uma funcionalidade nova no sistema, chamada de “Bad Words”, que nada mais é que um filtro de palavras “impróprias”. O que torna esse bug tão interessante é que o módulo de Bad Words gerava um erro muito inusitado, em um lugar desconexo, de forma confusa.
Para compreender melhor, a lógica era a seguinte:
- Utilizando o sistema de eventos do JForum 3, toda vez que um novo Post estivesse para ser inserido - ou seja, antes de persistir a mensagem em si -, o filtro de Bad Words era aplicado à mensagem, o qual ocasionalmente poderia modificar a mensagem original.
Até ai nada demais. O módulo de responder a mensagens existentes já existia a tempos, e com vários testes sendo aplicados. Tudo era gravado na mais perfeita ordem e exatidão. Porém, toda vez que o filtro de Bad Word entrava em ação, o sistema quebrava com um erro do Hibernate, acusando que havia objetos não salvos - o maldito “Transient Object Exception”. “Oras”, eu pensava, “mas é claro que o objeto é transiente. Eu estou adicionando ele agora, do que o Hibernate está reclamando?”. Isso sempre acontecia quando o módulo de Bad Words estava ativado. Se eu removesse ele, o erro parava.
Para tornar as coias mais claras, vale lembrar que Hibernate tem algumas regras de funcionanto bastante interessantes. Por exemplo, no momento em que chamamos session.save(entity), ele não executa o statement imediatamente, mas sim segura até uma ocasião melhor, que geralmente é o commit da transação ou um flush manual na sessão. Porém, o que muita gente não sabe é que, caso uma query qualquer - qualquer query mesmo, em qualquer entidade - seja executada antes do commit ou flush, o Hibernate força um flush na session automaticamente.
E por que isso é importante? Bem, porque a unica coisa que o filtro de Bad Word fazia era executar um badWordRepository.getAllBAdWords(), e nada mais. Se essa chamada fosse removida, o post era inserido com sucesso. Se ela fosse mantida, ocorria a Transient Exception. Estranho, não?! O código de reply a um tópico já existente era o seguinte:
-
public void reply(Topic topic, Post post) {
-
Topic current = this.topicRepository.get(topic.getId());
-
-
if (StringUtils.isEmpty(post.getSubject())) {
-
post.setSubject(current.getSubject());
-
}
-
-
this.performReplyValidations(post);
-
-
topic.setForum(current.getForum());
-
-
post.setTopic(current);
-
post.
setDate(new Date());
-
post.setForum(current.getForum());
-
-
if (!post.isWaitingModeration()) {
-
current.setLastPost(post);
-
current.getForum().setLastPost(post);
-
current.incrementTotalReplies();
-
post.getUser().incrementTotalPosts();
-
}
-
-
this.postRepository.add(post);
-
}
Eu costumo dizer que no JForum tudo tem uma razão de ser. Afinal, o sistema tem mais de 5 anos, e é extremamente estável. Muito do código existente está da maneira que está devido a muita ralação e testes, e por mais estranho que certas lógicas possam parecer, no fundo elas têm um sentido. Claro, não podemos aplicar isso ao sistema inteiro, mas é algo que se mostrou verdade inúmeras vezes. Nesse código ai, os testes passavam perfeitamente, e usando o sistema tudo funcionava perfeito. Por que então uma simples execução de query do Hibernate, em Bad Words, fazia acontecer um erro tão sem nexo como o Transient Object Exception?
Para quem conhece pouco de Hiberante, uma Transient Object Exception ocorre quando temos dois ou mais objetos dependentes entre si, e não salvamos algum deles, ai o Hibernate quebra dizendo “opa, espera ai. Você está me dizendo que quer salvar o objeto A, que tem uma dependência com o objeto B, mas você não está salvando o objeto B. Portanto, eu não sei o que fazer”. Ou algo parecido com isso.
Vale lembrar também que o filtro de Bad Words é invocado através do sistema de eventos do JForum 3, logo é natural que no código acima não haja uma chamada explícita a ele. O que é importante saber é apenas que, antes de ser persistido, a instância de post em questão é passada através de uma série de filtros, que podem ou não modificar o objeto em si.
Olhando e debugando o código do método reply(), não ficava claro a razão do bug. A transient object exception não parecia ter sentido algum. Afinal, o método recebia uma instância nova de um objeto Post, buscava no repositório o tópico atual, já existente, e associava o tópico com o novo post. Até ai tudo parecia normal - e, aliás, funcionava -, pois o tópico atual já estava na base de dados. então não deveria haver problema com objetos transientes. Depois de muito debugando, e tendo pedido a ajuda de várias pessoas, o Lucas Cavalcanti (colega de trabalho na Caelum) notou algo. Era uma luz para solucionar o problema.
Analizando o código com mais cuidado, vemos que o tópico atual é buscado no repositório, e armazenado na variável “current” (péssimo nome, deveria ser “currentTopic“). Mais abaixo, é feito “current.setLastPost(post)“. Ou seja, estamos associando o objeto novo (o post, no caso) com um objeto já existente. Hhhhmm, tudo bem, errado não está, pois logo em seguida chamamos o postRepository.add(post). Mas espera lá, antes de chamarmos add(post), o sistema de eventos é disparado, caindo na execução do método getAllBadWords() do módulo de Bad Words. E, como sabemos, a execução de uma query força o flush() automático no hibernate. E nesse ponto o tópico atual, representado pela variável “current”, foi associado a um objeto “post” novo (transiente), que AINDA não teve o seu save() invocado.
Bingo, tínhamos encontrado o bug. Faltava encontrar uma solução, que, graças a maneira de operar do Hibernate, foi bastante simples: mover a chamada a “postRepository.add(post)” algumas linhas para cima, ANTES da associação do post novo com o tópico atual. O código ficou assim:
-
// …
-
post.setForum(current.getForum());
-
-
// Código movido para cima
-
this.postRepository.add(post);
-
-
if (!post.isWaitingModeration()) {
-
current.setLastPost(post);
-
-
// …
Notem que a chamada ao método add() da postRepository foi feito antes da chamada ao setLastPost() da variável current. Bug resolvido. Tudo terminado? Não, falta o teste. A dificuldade nesse caso está na necessidade de testar a ordem das invocações. Sorte que temos Mocks para isso. A solução adotada foi então mockar o objeto current, o objeto postRepository, e garantir que a chamada ao método add() de PostRepository fosse feita antes da chamada ao método setLastPost() de Topic. O código do teste ficou assim:
-
@Test
-
public void replyPostRepositoryShouldBeCalledBeforeCurrentTopicSetLastPost() {
-
final Sequence sequence = context.
sequence("order");
-
final Post post = new Post(); post.setSubject("subject"); post.setText("msg");
-
post.setUser(new User());
-
-
context.checking(new Expectations() {{
-
Topic topic = context.mock(Topic.class);
-
one(topicRepository).get(1); will(returnValue(topic));
-
-
one(postRepository).add(post); inSequence(sequence);
-
one(topic).setLastPost(post); inSequence(sequence);
-
-
allowing(topic).getForum(); will(returnValue(new Forum()));
-
one(topic).incrementTotalReplies();
-
}});
-
-
Topic topic = new Topic(); topic.setId(1);
-
topicService.reply(topic, post);
-
-
context.assertIsSatisfied();
-
}
Um muito obrigado ao Lucas pela ajuda na solução deste bug :).
November 24th, 2008 — Geral
Quando uma ou mais pessoas que você confia e / ou costuma pedir opiniões diversas diferem em um assunto, em qual delas confiar? Ou devemos juntar um pouco de tudo e tirar a nossa própria conclusão?
Esse questionamento, que muitos de nós provavelmente passamos frequentemente, muitas vezes acaba gerando mais confusão ainda - ou melhor, mais indecisão. É como o Paradoxo das Escolhas, onde uma grande gama de opções - seja de roupa, de tênis, de cor ou qualquer outra coisa - é pior para o usuário / consumidor do que uma gama pequena, pois ele ficará indeciso sobre o que escolher.
Quando temos uma determinada opinião, independentemente da maneira como foi formada, e discutimos com outra pessoa a respeito do assunto, informações divergentes acabam surgindo, e em casos onde você ainda está formando uma base de conhecimento, saber diferir o “certo” do “errado” (sim, pois mesmo pessoas muito bem informadas podem estar erradas) acaba resultando em mais questões que o número original.
Isso tudo nos força, de uma maneira ou de outra, a ir atrás de mais e mais informação (e aqui tentando evitar a palavra “conhecimento”), o que pode levar a mais e mais dúvidas. Sorte (?) que em muitos casos podemos tirar nossa própria conclusão na prática. Quando isso não for possível, nosso senso de julgamento será posto a toda prova.