langs, bashes e encodings da vida

Acho que posso dizer sem medo de errar que na lista de dores de cabeças que meus projetos pessoais e startups me trouxeram, a questão da codificação de strings está tranquilamente entre os TOP 3. E quando eu achei que tudo nesta área estava resolvido, eis que me apareceu um novo bug do nada que me fez perder algumas horas de mal humor. Porque coisas que funcionam por meses e de repente param de funcionar sem motivo aparente em geral me tiram do sério.

O SigaSeuTime (cujo novo site acaba de estrear, se me permitem o autojabá) possui dois bots IM, GTalk e MSN, que fazem parte do conjunto de plataformas para distribuição de contéudo. Ambos os bots são escritos em Java, e a plataforma é escrita em Python. Para fazer a comunicação entre os subsistemas eu uso ou REST ou comunicação via UDP. No caso das notícias em tempo real, acabei optando por UDP para evitar pooling desnecessário. E isso funcionou perfeitamente nos últimos 6 meses: um agente se encarrega de puxar as notícias, processar, filtrar e gravar na base de dados. Outro agente lê as notícias novas e manda pros bots via UDP, para envio aos usuários online. .

Até que de repente, as mensagens começaram a aparecer tanto no MSN quanto no GTalk  com a acentuação totalmente errada. Típico resultado de strings codificadas de um jeito e interpretadas de outro (tinha que existir um jeito fácil e direto de se descobrir a codificação de uma string sem a necessidade de metadados)! Olhando para o histórico do meu SVN vi que as últimas modificações em códigos relativos aos componentes envolvidos neste processo datavam de no mínimo 3 meses.

Ou seja, só podia ser culpa do meu servidor, ou do alinhamento planetário, ou então do Bush! Não não…a culpa, obviamente era minha. Sempre é, por mais que a gente lute contra isso.

A causa do problema estava em um script SHELL que eu criei recentemente cujo objetivo é de reiniciar os bots em caso de falha de comunicação com os servidores. Até então, o processo de reboot era manual, feito por mim mesmo via shell SSH, e a função deste script é de automatizar o processo, matando e executando novamente o bot.

Inofensivo certo? Nem tanto. O script é executado periódicamente pelo crontab do sistema operacional, e foi aí que o problema nasceu: as variáveis de ambiente do cronjob são diferentes das variáveis de ambiente do SHELL SSH, e em particular o segundo possui a variável LANG=en_us.UTF-8, que é justamente a codificação usada internamente pelo sistema. Vivendo e aprendendo: isto influi diretamente na JRE, e a falta da variável fez com que o sistema assumisse outra codificação (provavelmente ISO Latin 1). A solução foi exportar a variável no meu script.

News4Dev

Um dos objetivos da família 4Dev é oferecer um conjunto de ferramentas e plataformas onde profissionais de TI se encontrem e troquem informações. E é nesta linha que adicionamos mais um sistema: o news4dev.

A idéia é simples: os usuários enviam links, e o sistema permite que outras pessoas votem nas notícias favoritas e comentem. Um misto de forum com sistema de recomendação.

Quanto mais gente participar, melhor.

Job4Dev no Você S/A

A Você S/A de fevereiro publicou uma artigo sobre como encontrar vagas de emprego no Twitter, e citou o job4dev como fonte, dentre outras. A materia completa pode ser lida aqui.

Job4Dev citado na Você S/A de Fevereiro

Capa da Você S/A de Fevereiro

Só não foi perfeito porque na página ao lado havia uma matéria sobre sites de empregos, e nesta não nos citaram.

Mas tudo bem. Os pais estão bem orgulhosos! De grão em grão, vamos ganhando mercado.

E agora, rumo ao mercado internacional.

Fatos sobre Erlang

Compilação de alguns pontos relevantes sobre Erlang, feita por Odracir:

  • Linguagem de programação de propósito geral, com ambiente de execução (runtime environment) implementado numa máquina virtual;
  • Possui suporte “embutido” para concorrência, distribuição, e tolerência à falhas.
  • É “Open-Source”, distribuído através da licença “ERLANG PUBLIC LICENSE“, que é essencialmente a Mozilla (Netscape) Public Licence, porém com algumas poucas modificações para torna-la compatível com a legislação Sueca.
  • É escrito em C, e é  portável para diversas plataformas.
  • Muito escalável!  Sua máquina virtual é responsável pela criação de “processos leves” de forma  rápida e barata, tanto em termos de utilização de recursos de CPU como de memória. Em outras linguagens de programação, normalmente estes serviços são delegados para o sistema operacional, o que limita  o número máximo de processos simultâneos permitidos.
  • Para modelar um sistema um Erlang nós usamos: “COP – Concurrency Oriented Programming”.
  • Não existe estado compartilhado…
  • Variáveis podem ser atribuídas apenas uma única vez. (Single assignment variables)
  • Os processos se comunicam entre si através do envio de mensagens (cópias imutáveis) assíncronas.
  • Tipicamente um código escrito em Erlang faz uso dos diversos núcleos disponíveis nas CPUs modernas, praticamente sem codificação adicional. Testes mostram que clusters com 16 máquinas executam um programa 14,55 vezes mais rápido do que o mesmo programa sendo executado numa única máquina, sem codificação adicional ou específica para isto!
  • Permite a interoperabilidade com C,C++, Java, Ruby, Python, Perl, etc …
  • Estudos mostram que para um mesmo projeto, o código Erlang é de 4 à 10 vezes menor que seu equivalente escrito em C++.
  • Possui a biblioteca  OTP (Open Telecom Platform).
  • Em situações extremas de “carga”, um sistema Erlang tende a degradar seu desempenho, mas não para de funcionar.

Porque ando tão calado

Technology is nothing. What’s important is that you have a faith in people, that they’re basically good and smart, and if you give them tools, they’ll do wonderful things with them. It’s not the tools that you have faith in — tools are just tools. They work, or they don’t work. It’s people you have faith in or not. Yeah, sure, I’m still optimistic I mean, I get pessimistic sometimes but not for long.

Steve Jobs – 1994

Diante do bombardeio diário de novidades, um computeiro deve se sentir tentado a acompanhar todas as discussões sobre os produtos e as tecnologias da moda: no último meses tivemos um ou dois iPhone killers, uma linguagem de programação “abençoada”, o estouro do Cloud Computing, um “sistema operacional“,  o fim da indústria editorial e o surgimento do computador para todos os outros.

E nós aqui, passamos boa parte do ano calados. Trabalhando. Até poderíamos ter escrito N+1 blog posts opinando sobre cada uma dessas novidades. Mas nenhuma dessas nos ajudou a entender melhor o mundo, ou a construir algo melhor. Então, passou batido. Sem contar que estávamos muito mais interessados em produzir algo de fato, ao invés de simplesmente sermos mais um palpiteiro na poltrona.

É uma revolução por minuto, que muitos tentam discutir, comparar, medir, organizar. É tudo bobagem. É tudo tecnologia, e tecnologia não é um fim em si mesmo. Por favor, saiam do Google Wave, parem de perder o presente discutindo o futuro. Construir o futuro é muito mais divertido.

Job4Dev 3.0

Primeiro post do ano, e nada melhor do que começar com novidades (antes tarde do que nunca)!

Os assinantes de RSS do Job4Dev mais atentos devem ter percebido uma mensagem nova que passou a aparecer no rodapé de cada anúncio. Cito:

Parece que você está se cadastrando (ou prestes a se cadastrar) para o feed de todos os anúncios. Nenhum problema por nossa parte, mas tenha em mente que o volume de anúncios pode ser excessivo. Você pode se interessar em dar uma olhada em nossa página para filtrar os tipos de empregos, e obter feeds específicos, baseado na localização e nas palavras-chaves.
Alguns talvez tenham visto a mensagem em inglês. Fato é que algo parece ter mudado. E mudou: graças ao trabalho do Raphael, o Job4Dev evoluiu e chegou à versão 3.0 com grandes pretensões. Job4Dev pretende virar global, mas mantendo a qualidade local: ruído ZERO, essa é a nossa meta!

Aqueles que frequentam o site  devem ter notado que mais vagas internacionais estão sendo cadastradas, e que um versão em inglês está disponível. Acessando a URL http://www.job4dev.com.br, o site estará sempre em português, mas ao acessar http://job4dev.com, a língua irá variar com as preferências do seu navegador.

Para tentar oferecer as vagas mais próximas do visitante, adicionamos um mecanismo de georeferenciamento na página principal: o Job4Dev descobre a sua localização e mostra vagas da cidade, ou da província (estado) ou do país. Por exemplo, neste momento em que vos escrevo, estou em Sampa e ao acessar o site, vejo apenas vagas para a capital do estado. Está em Sampa mas quer procurar vagas no Rio ou em Campinas? Sem problemas: clique em no link Vagas (ou Jobs), e veja a listagem completa de anúncios, com opções de filtros por palavra-chave, tipo de contrato, localização e por aí vai.

Por falar em filtros, a partir de agora é possível agora criar feeds RSS específicos para suas necessidades. Entre no site, crie o filtro desejado e clique no link feed, que aparecerá no quadro amarelo no topo da página.

Em relação às vagas e empresas, aumentamos o nosso lado colaborativo, adicionando um mecanismo de comentários para usuários cadastrados. Gosta da empresa? Quer dar mais informações? Acha que a vaga vale a pena? Comente. Assim, estamos promovendo a criação de uma base de dados de empresas de tecnologia do mundo todo, permitindo uma escolha altamente consciente e embasada em opiniões alheias.

O formulário de cadastro agora diferencia usuários que buscam vagas daqueles que desejam cadastrar novas vagas. Um passo a mais na oferta de serviços específicos para cada um destes públicos, como por exemplo o cadastro de currículos online (já disponível também para usuários cadastrados).

Vale citar também pequenas modificações, como a simplificação do fluxo de cadastro de anúncios (com link para adição de vaga diretamente da página de empresa), e mecanismo para troca de senha (esta que já veio tarde demais….mas novamente: antes tarde do que nunca).

Resumindo, é isso! Feliz 2010 para todos.

Normalização de sequências de caracteres

Um dos motivos que diminuiram minha participação neste blog nos últimos meses foi o meu trabalho intenso no SigaSeuTime. Entre os sócios no projeto, sou o único desenvolvedor, e a lista de tarefas não para de crescer. Gostaria de contar aqui um problema simples que tive que resolver, e que pode interessar outros desenvolvedores.

No mês passado, colocamos no ar dois bots, um para o MSN e outro para o GTalk/Orkut, que podem ser utilizados como alternativa para o Twitter para receber notícias e jogos ao vivo. Os bots funcionam como usuários normais: você adiciona o dito cujo na sua lista de amigos (usando o email sigaseutime@siga.st) e passa a receber mensagens. Ao contrário do Twitter, onde cada canal representa um time (@sigaFlamengo, @sigaVasco, @sigaCorinthians, etc…), nos bots um usuário pode selecionar um ou mais times, enviando a mensagem siga <nomedotime>.

Internamente, eu mantenho duas versões de nomes para cada canal do sistema: o nome completo (Corinthians, São Paulo, Palmeiras, Santo André, Seleção Brasileira, etc..) e um slug (corinthians, saopaulo, palmeiras, santoandre, selecaobr). Pra quem não sabe, um slug pode ser definido como uma versão URL Friendly do nome: sem acentos, espaços em branco e caracteres não ASCII. Originalmente, os bots aceitavam o uso de um dos dois nomes no comando. Por exemplo, siga Santo André ou siga santoandre.

Obviamente, fomos recebendo feedbacks de usuários que não conseguiam cadastrar um time. Em geral, isto acontecia com times com nomes compostos e/ou acentos. Analisei o log e percebi que era muito comum alguém digitar siga sao paulo por exemplo. Como o cliente tem sempre razão, decidi que um algoritmo mais espertinho de leitura do nome se fazia necessário: criei então uma funcionalidade de conversão de nomes para slugs. A primeira parte foi fácil: colocar tudo em letras minúsculas, remover espaços e outros simbolos não conformes.

Faltava a parte mais importantes: converter caracteres acentuados em caracteres não acentuados. afinal, para efeito de comparação, são e sao deveriam ser equivalentes. Inicialmente pensei em criar uma tabelinha de conversão simples, mas desisti porque a chance de alguém digitar um caso não contemplado era muito grande. Fui então pesquisar na Internet, e descobri que o UNICODE já prevê esta funcionalidade, e já tem tudo mapeado. Ainda bem que existem cientistas da computação prevenidos no mundo!

Este recurso se chama Normalização (como não pensei nisso antes….), e oferece 4 formas distintas: NFD, NDC, NFKC, e NFKD. Cada uma tem suas particularidades, e aconselho a quem se interessar pelo assunto pesquisar no site do UNICODE: http://www.unicode.org/faq/normalization.html.

O essencial é entender que existe um mapeamento de caracteres compostos (por exemplo ç = c + cedilha, ou ã = a + ˜), e que existem bibliotecas em algumas linguagens que implementam este mapeamento e que podem ser utilizadas para fazer a conversão. Vou dar um exemplo em Java e em Python:

Java:

import java.text.Normalizer;
import java.text.Normalizer.Form;
Normalizer.normalize(string, Form.NFD);

Python:

from unicodedata import normalize
unicodedata.normalize(forma, string)#forma pode ser 'NFC', 'NFD', 'NFKC' ou 'NFKD'

Utilizando este recurso, eu consigo facilmente converter SANto André, Santo Andre, SANTOandre ou qualquer outra combinação para o formato que eu espero: santoandre.

Histórico no Admin do Django

A funcionalidade de geração automática (de qualidade) de uma área de administração em projetos Django foi talvez a grande responsável por eu preferir esse framework ao Turbogears (junto com o fato do Turbogears ser na verdade uma federação de pequenos projetos). Pode parecer bobo, mas desenvolver uma área de admin bem feita é custoso, e ter uma versão básica bem feita de graça pode ser um grande diferencial na hora de construir algo novo.

Dentre as várias funções interessantes oferecidas por este admin, o histórico de operações sobre os objetos merece destaque:  cada ação efetuada sobre todo e qualquer objeto através do admin é gravada em uma tabela de histórico gerada automaticamente (data, usuário, objeto modificado, natureza da modificação), que pode ser visualizada em uma tabela web.

Infelizmente, este histórico é gerado automaticamente apenas para ações efetuadas através do admin. Recentemente, tive a necessidade de gravar o histórico de operações efetuadas por tarefas em background no SigaSeuTime (envio de anúncios comerciais para o Twitter), que me permitissem controlar de forma unificada e centralizada o bom funcionamento do sistema.

Pesquisando um pouco, descobri como fazer isso de forma simples com um código pequeno, que vou reproduzir abaixo:

from django.contrib.admin.models import LogEntry
from django.contrib.contenttypes.models import ContentType

def gravahistorico(objeto, mensagem, usuario): LogEntry.objects.logaction( userid         = usuario.id, contenttypeid = ContentType.objects.getformodel(objeto).pk, objectid       = objeto.pk, objectrepr     = mensagem, # Message you want to show in admin action list changemessage  = mensagem, # I used same action_flag     = 4 )

Chamando este código, ações sobre um objeto serão gravadas na tabela de histórico, e poderão ser vista na área de administração do Django. Simples e extremamente útil.

Código fluente

Com o advento e crescimento dos projetos Open Source, se intensificou o conceito de que em última instância a melhor documentação de um projeto é o seu próprio código fonte. Concordo plenamente com isso! Em projetos ativos, manter uma documentação sobre arquitetura completamente atualizada é uma tarefa ingrata (sobretudo se seguirmos os moldes de artefatos exigidos pelas metodologias waterfall), e documentação errada é a pior coisa que pode existir.

Em projetos Open Source, onde os recursos são muitas vezes escassos e não se pode dar o luxo de manter uma pessoa apenas para manter tudo atualizado, a necessidade de uma documentação simplificada impera. E a necessidade de um código organizado também.

O problema é que muita gente levou ao pé da letra esse conceito, e chegou-se à conclusão de que comentários são supérfluos, afinal o próprio CÓDIGO é a documentação. Resultado: tenho cada vez mais visto toneladas de linhas de código sem um puto de um comentário.

Ah, mas com nomes incrivelmente sugestivos: apagaORegistroDoUsuarioEMandeMensagemdeDesolePraEle.

Senhores e Senhoras, o código fluente!

Plagiando um exemplo que eu peguei no iMasters:

Vendedor.deNome("Abreu").
                 vende().
                 paraCliente("Rafael").
                 oProduto("Mouse Verde").
                 oProduto("Teclado de Pano").
                 comDescontoDe(20).
                 mostrandoDetalhes();
Não posso dizer que não entendo o que este código faz! Até minha avó de 92 anos entenderia. Lindo.

O problema é que por mais que se empacote todas as funções com nomes extremamente sugestivos, que se crie DSLs para se esconder a complexidade,  que as variáveis sejam extremamente bem nomeadas, alguma hora os detalhes sórdidos do código vão começar a aparecer: bases de dados, queries, estruturas de dados, rede, integrações com outra bibliotecas, etc…

(Aaaaaaaaa, você não trabalha com bibliotecas, nem com BD, nem com rede, e apenas integra código fluente? Meus pêsames, o mundo da computação é bem mais interessante e divertido do que isso. Próximo…)

E na hora que chegamos aos detalhes sórdidos, por mais que o nome do método explique o que está se fazendo, pode ficar a dúvida: porque o método faz isso desta forma? E entender o porque das coisas é essencial se você está tendo que corrigir/modificar/aprimorar/integrar/analisar código legado. Sim, porque o que torna o código legado tão infernal de se mexer é justamente entender O QUE SE PASSAVA PELA CABEÇA DO DESGRAÇADO QUE ESCREVEU AQUILO!

Exercício: pegue um código que você escreveu há 6 meses atrás, de um projeto do qual você não participa mais, e tente entender todas as minúcias….foi sofrido? Imagino que sim. Agora imagine o pobre coitado do estagiário que está tendo que resolver um bug que ocorre naquele trecho!

Muito se fala por aí que código tem que ser escrito para ser lido por seres humanos e não por máquinas. Uma máquina apenas interpreta aquilo que escrevemos, e não está nem aí se faz sentido ou não. Ao contrário de seres humanos. Todos ficamos muito felizes quando entramos no javadoc da Sun, e encontramos alguns detalhes de implementação interessantes de métodos cujos nomes muitas vezes são bem explicitos. Como por exemplo o remove(String key) de uma Collection. Ninguém tem dúvidas do que ele faz, mas o que acontece se eu passar um valor nulo? Ou se passar um objeto que não existe?

Comentários são uma ferramenta muito útil, presente em qualquer linguagem, e que fazem parte do código.  Devem ser utilizados com inteligência, para facilitar a vida de todo mundo, tornar o código realmente uma documentação completa e inteligente e poupar horas, cabelos e neurônios de desenvolvedores cansados, estressados ou que simplesmente não querem passar horas decifrando a mente alheia.

Babel

Eu já escrevi uma boa quantidade de linhas de código na minha vida. Talvez 100k, talvez mais. Não faço idéia. Com certeza este número diminuiu muito depois de eu ter começado a programar em Python :-) .

E quando releio alguns destes códigos, considero que na média a qualidade é boa (e pelos feedbacks de colegas, outras pessoas devem concordar com isso). Mas se me perguntarem o que me incomoda no meu estilo de codificar (sim, porque codificar é muito parecido com escrever livros: requer prática, cultura geral, leitura de outros textos, etc…) eu responderei na lata: a minha total falta de padronização da língua utilizada na nomenclatura de classes, métodos, atributos. Até hoje eu não me decido por escrever 100% em inglês, ou 100% em português. E faço isso sem perceber.

Em tempos de internet, globalização, software livre e colaboração, me agrada a idéia de existir uma língua universal para desenvolvimento de código. Facilita o intercâmbio de informações com pessoas de outros países, permite que se entre em mundos diferentes, quebrando a barreira da língua.

E inglês é a língua ideal para isso, simplesmente porque ela é a lingua internacional de fato (talvez um dia tenhamos que codificar em mandarin, mas isso é tema pra outra conversa). Além disso, boa parte das linguagens hoje (perto de 100% eu diria) utilizam primitivas e bibliotecas em inglês, para não se falar dos padrões e codificação. Por exemplo, em Java é padrão usar getters e setters para se ter acesso à informações internas a um objeto. Fica razoavelmente ridículo escrever getMinhaVariavel…mas ainda assim, eu faço isso com certa frequência.

Isto porque,  pior do que ter um código não universal (escrito em português por exemplo) é ter um código escrito em inglês errado. Eu com certeza já escrevi vários métodos que soariam ridículos para um falante nativo, e já li uma tonelada que soavam ridículo até pra mim. O problema é que, apesar de ler constantemente textos de computação em inglês e me virar bastante bem na língua do Tio Sam, na hora do cansaço ou da necessidade de termos mais rebuscados, as vezes o cérebro pede arrego. E aí vai em português mesmo. Já tive casos de precisar codificar conceitos ligados ao plantio de eucaliptos para produçao de papel, e fui atrás de termos em inglês para colheita, plantio, talhão, gleba….o resultado foi bom, mas tomou um bom tempo.

Ou seja: tenho que aprimorar meu inglês.

Em tempo: recentemente dei uma folheada em um livro sobre SCRUM, escrito em português por um brasileiro, e desisti de comprar o dito cujo quando vi um “printar” no meio de uma frase. Parece que do mesmo jeito que as vezes eu tenho preguiça de procurar termos em inglês, certas pessoas tem preguiça de escrever direito em português.

Last, but not least: a questão de escrever em inglês entrou em pauta várias vezes no Log4Dev. Temos pelo menos um expert no assunto, Lullis, tradutor nas horas vagas. Com certeza, mudar de língua tornaria nosso blog internacional, e fama e riqueza bateriam às nossas portas. Mas, por modéstia, vontade de manter uma vida calma e anônima, e falta de proficiência absoluta, decidimos manter os textos em português. Antes uma idéia bem transmitida para poucos do que mal escritas para a humanidade.

Próxima Página »

Switch to our mobile site