Blog

  • Valor de uma idéia não executada: R$ 0,00

    Desde que saí do melhor-emprego-do-brasil lá no INdT, estou me aventurando no mundo do empreendedorismo.

    A minha empresa é a Triveos e o meu plano, ao criá-la era o de prestar serviços e consultoria em desenvolvimento de software e com o dinheiro obtido com essa atividade investir no desenvolvimento de uma Webapp para gestão de micro e pequenas empresas.

    Mas, como já diz o chavão, empreender não é fácil. E não digo que no Brasil seja muito mais difícil como dizem (isso vale outro post).

    Mas aos trancos-e-barrancos a empresa está funcionando, o projeto em questão foi aprovado no programa PRIME da FINEP e até estamos trabalhando em um projeto-rápido, em paralelo, para garantir uma fonte de receita rápida e garantida que nos ajude no desenvolvimento do projeto principal.

    Bom… agora que estamos todos ‘contextualizados’, vamos para o assunto principal.

    No dia-a-dia da Triveos nós costumamos ter “idéias” interessantes para novos projetos. Eu anoto rapidamente essas idéias em fichas pautadas e as deposito no arquivo “Maybe Someday” de projetos.

    Uns meses atrás eu tive a idéia de um produto relativamente simples de ser implementado e que permitiria a empresas construirem um ‘workflow’ para operacionalizar as suas relações com as redes sociais (no caso o Twitter).

    Uma empresa poderia, então, ter várias pessoas trabalhando em nome da empresa nas redes sociais mas tudo ainda poderia ser ‘revisado’ antes de ir ao ar.

    Assim… fazer um produto-mínimo-viável disso, em Python/Django e rodando no GAE não levaria mais do que 2 semanas de um desenvolvedor.

    Uma idéia boa (eu acho), simples (como são as melhores idéias) e fácil de ser implementada. Uma idéia dessas parece valiosa, não? Só parece.

    Ter tido essa idéia e anotá-la não ajudou ninguém nem rendeu nada (dinheiro, fama, sucesso, mulheres, etc).

    Se eu tivesse trabalhado nessa idéia, um pouquinho todo dia, e colocasse ela no ar eu teria ganhado ao menos uma coisa: experiência, aprendizado. Também poderia ajudar empresas que tem esse tipo de problema e, com isso, faturar um cascalho.

    E com o tempo foi isso o que aconteceu. Mas não comigo. O pessoal da Kingo Labs criou o http://trmanager.com.br que, na essência, faz a mesma coisa que pensei quando tive a tal idéia (mais funcionalidades extras).

    A Kingo Labs “roubou” a minha idéia? No way. A única ‘testemunha’ que tenho é o meu sócio na Triveos. Só com ele eu falei sobre essa idéia.

    Isso também pode ser usado pra afirmar que ter idéia é “fácil pra caramba” (parafraseando a propaganda da Embratel). Aliás, quando você está trabalhando num projeto, as idéias são tantas que chegam a atrapalhar o trabalho.

    O valor de uma idéia é, então, obtido com a fórmula:

    Valor = Idéia x Execução1

    Se entre os programadores (do mundo Linux) a frase “Talk is cheap, show me the code” faz sentido, no mundo das idéias ela também faz.

    1 Já vi a palavra Inovação no lugar de Valor, mas a palavra Inovação anda muito desgastada ultimamente.

  • Corinthians no Orkut

    Corinthians no Orkut

    Meus amigos sabem que sou corintiano. Torcedor que acompanha o time, assiste aos jogos (menos no estádio por questões de segurança), veste a camisa e tudo o mais. Também sabem que trabalho com desenvolvimento de software e que sou heavy user de Internet.

    Graças a esses contatos na rede fui um dos primeiros brasileiros a receber um convite para participar de um site novo do Google conhecido por “Orkut”. Era uma época onde comíamos dezenas (centenas até) de “donut’s” que não eram entregues aos servidores.

    Os primeiros usuários do Orkut no Brasil estavam localizados no Sul (majoritariamente Porto Alegre) e em São Paulo (majoritariamente na Capital). A primeira comunidade que eu criei foi a “Corinthians” quanto só existia a do Internacional e do Grêmio. Sério! As primeiras comunidades de time de futebol do Brasil foram a do Colorado e a do Tricolor Gaúcho. Depois que eu criei a comunidade do Corinthians abriram as porteiras do Orkut (podiamos usá-lo sem convites) e outras comunidades foram criadas.

    Como era de se esperar as comunidades do Flamengo e do Corinthians cresceram mais do que as outras. Mas a comunidade do Corinthians, contradizendo certas pesquisas feitas por cariocas, sempre teve mais membros do que a do Flamengo.

    Como era de se esperar não demorou muito para começar as pixações. As ferramentas de moderação e de administração de comunidades do Orkut se limitavam à aprovação um a um dos inscritos na comunidade e a exclusão dos baderneiros (sem opção de banir).

    A coisa ficou “feia” quando chegamos a 600.000 integrantes e uma média de 300 novos inscritos por *dia*. Todos devidamente moderados somente por mim num processo que me obrigava a visitar perfil por perfil dos novos usuários para ver se o usuário já não estava em outra comunidade de time de futebol (o torcedor entra pra comunidade do seu clube antes de entrar na dos clubes adversários para pixar).

    Não dava mais. Eu passava o dia inteiro mexendo só com isso num trabalho não-remunerado (ter uma comunidade desse tamanho, naquela época, não rendia dinheiro algum).

    Chegou a hora de tomar algumas medidas: pedir ajuda na moderação e criar regras para lidar com os arruaceiros que chegavam às centenas após um jogo.

    Chamei meus amigos corintianos Érico (que torce pro Juventus nas horas vagas) e Márcio Medrado para me ajudar na moderação. Na época só um usuário podia administrar uma comunidade. Criamos o usuário “Gilmar Giovanelli” para essa função e distribuímos a senha entre nós. Esse problema estava resolvido faltava resolver o problema das pixações.

    Entrei em contato com os moderadores das comunidades do Flamengo, Palmeiras, Santos e São Paulo pra perguntar a eles como faziam para resolver o problema dos “ataques” e me disseram que lidavam com aquilo caso-a-caso numa hercúlea tarefa de enxugar gelo. Exatamente o que estávamos fazendo.

    Conversa vai, conversa vem, sugeri criar regras comunitárias válidas para todas as nossas comunidades. Essas regras eram discutidas na comunidade “Clube dos 13”.

    A regra mais importante dizia que quando, por exemplo, um flamenguista invadia a comunidade do Corinthians para tumultuar ele era banido da comunidade do Corinthians e uma solicitação era feita no Clube dos 13 para ele ser banido da comunidade do seu próprio clube. Isso funcionou lindamente por muito tempo. Só não sei se ainda funciona.

    Chegamos a 800.000 membros, a segunda maior comunidade da categoria “Sports & Recreations” do Orkut. Só perdiamos para “Eu adoro praia” (concorrência desleal :D). Os outros seguiam: Flamengo, São Paulo, Palmeiras e Santos.

    Mas a história teve um final triste: por conta de um bug no Orkut (um?) as comunidades perderam os moderadores e uma mensagem “become a moderator” surgiu na comunidade do Corinthians (na do Flamengo também). No caso do Flamengo um usuário pegou a moderação e transferiu devolta para o antigo dono. No nosso caso o usuário que assumiu a moderação brigou com alguns membros porque não queria devolvê-la ao “Gilmar” e apagou a comunidade.

    Tentamos de todas as formas contatos com a equipe do Orkut para pegar devolta a comunidade mas nada feito.

    Não demorou muito e outra comunidade Corinthians foi criada. As pessoas foram voltando, mas mesmo assim não era mais “aquela” comunidade. O lado bom disso: o trabalho era grande, difícil e não-remunerado. Tiramos um peso muito grande das costas.

    Por outro lado, imagina a “influência” que teríamos hoje, em tempos de “Marketing Social”, ter uma comunidade com cerca de um milhão de membros? 🙂

    (PS. esse post surgiu a partir da minha ideia de recriar a comunidade Corinthians no novo Orkut. Mas para isso eu preciso de um convite :P)

  • Home, End, Page Up and Page Down configuration for Mac (Snow Leopard)

    Terminal.app

    Enter “Terminal->Preferences->Settings->Keyboard“, and change the following options (press <ESC> to get \033 in text below):

    Vi Mode

    Home: \033[H
    End: \033[F  
    PageUp: \033[5~  
    PageDown: \033[6~
    

    Emacs Mode

    Home: \033[1~
    End: \033[4~
    PageUp: \033[5~
    PageDown: \033[6~
    

    Bash/Readline

    Edit your ~/.inputrc and add the following lines:

    Common settings

    # Be 8 bit clean.
    set input-meta on
    set output-meta on
    set convert-meta off
    
    # allow the use of the Delete/Insert keys
    "e[3~": delete-char
    "e[2~": quoted-insert
    
    # mappings for "page up" and "page down"
    # to step to the beginning/end 
    # of the history
    "e[5~": beginning-of-history
    "e[6~": end-of-history
    

    Vim mode settings

    # Vi mode
    set editing-mode vi
    set keymap vi
    
    # allow the use of the Home/End keys
    "e[H": beginning-of-line
    "e[F": end-of-line
    

    Emacs mode settings

    # allow the use of the Home/End keys
    "e[1~": beginning-of-line
    "e[4~": end-of-line
    

    Vim

    Edit the file ~/.vimrc and add the following options:

    Terminal configured with Vi mode

    map <Esc>[H <Home>
    imap <Esc>[H <Home>
    map <Esc>[F <End>
    imap <Esc>[F <End>
    map <Esc>[5~ <PageUp>
    imap <Esc>[5~ <PageUp>
    map <Esc>[6~ <PageDown>
    imap <Esc>[6~ <PageDown>
    

    Terminal configured with Emacs mode

    map <Esc>[1~ <Home>
    imap <Esc>[1~ <Home>
    map <Esc>[4~ <End>
    imap <Esc>[4~ <End>
    map <Esc>[5~ <PageUp>
    imap <Esc>[5~ <PageUp>
    map <Esc>[6~ <PageDown>
    imap <Esc>[6~ <PageDown>
    

    Cocoa Keybindings

    Put the following lines in ~/Library/KeyBindings/DefaultKeyBinding.dict:

    {
        /* home */
        "UF729" = "moveToBeginningOfLine:";
        "$UF729" = "moveToBeginningOfLineAndModifySelection:";
    
        /* end */
        "UF72B"  = "moveToEndOfLine:";
        "$UF72B" = "moveToEndOfLineAndModifySelection:";
    
        /* page up */
        "UF72C"  = "pageUp:";
        "$UF72C" = "pageUpAndModifySelection:";
    
        /* page down */
        "UF72D"  = "pageDown:";
        "$UF72D" = "pageDownAndModifySelection:";
    }
    

    Firefox

    Install the following Firefox extension.

    Textmate

    I strongly recommend to Textmate users to disable the “Smooth Scrolling” option in System Preferences->Appearance->Use smooth scrolling. This option cause a strange behaviour with Page Up/Down on Textmate.

    References

    Update: Added suggestion to disable “Smooth Scrolling” for Textmate users.

  • É mais fácil pedir desculpas do que permissão

    Diferente do que escrevi no post Dicas para um bom programa em Python, onde eu dou dicas de como proceder para ter um programa Python melhor, desta vez vou falar sobre um estilo que prefiro. Não quero dizer que estou certo ou errado, apenas que prefiro assim.

    It’s easier to ask forgiveness than it is to get permission

    Grace Hopper

    Recentemente, dentro do tempo que me sobrava, comecei a desenvolver uma biblioteca pra fazer requisições HTTP para uma API REST. Essa biblioteca seria usada para criar testes automatizados do projeto que iremos começar a desenvolver aqui na empresa.

    Essa biblioteca faria basicamente o mesmo que a httplib e httplib2 do Python mas com algumas conveniências: (de)serialização de JSON/XML, conteúdo calculado no momento da request (ex. assinatura da requisição), e uma classe “TestCase-like” com funções que auxiliassem no desenvolvimento de testes.

    Eu tinha só algumas idéias do que essa biblioteca faria e quase nada de código quando vi o lançamento do Bolacha, desenvolvido pelo Gabriel Falcão, no Planeta Globo.com. Guardei o link pra conferir depois pois poderia ser útil para o que eu queria fazer.

    Ontem eu tive um tempo para analisar e vi que ele não só seria útil como já fazia a parte mais essencial do que eu precisava (requisições GET/POST/PUT/DELETE/…).

    Como o projeto esta hospedado no github.com tratei logo de fazer um fork para criar as outras funcionalidades que eu precisava. Código legal, código simples, código bem feito, mas… quando encontrei os primeiros…

    def request(self, url, method, body=None, headers=None):
      if not isinstance(url, str):
        raise TypeError(f'Bolacha.request, parameter url must be a string. Got {url}')
    
      if not isinstance(method, str):
        raise TypeError(f'Bolacha.request, parameter method must be a string. Got {method}')
    
      if method not in HTTP_METHODS:
        raise ValueError(f'Bolacha.request, parameter method must be a valid HTTP method. Got {method}. {RFC_LOCATION}')
      
      # ...continua

    … notei que o estilo do Gabriel divergia do meu. Nada errado com isso. Tanto que, mesmo assim, continuarei a usar e melhorar o Bolacha mantendo (dentro do possível) o mesmo estilo original do autor para que ele possa aceitar minhas contribuições.

    O que não gosto desse estilo é que, com ele, sempre estamos pedindo permissão, ou seja, verificando de antemão alguma informação no lugar de usá-la imediatamente e, só em caso de erro, considerá-las inválida. Nesse caso estamos adicionando um overhead desnecessário (muito pequeno neste exemplo) até mesmo para casos de uso correto do método.

    Outro problema que temos nesse estilo reside no fato de que estamos usando o mecanismo de herança da linguagem como um sistema de criação de interface para o objeto. Se eu quiser passar um objeto que se comporta exatamente como uma string mas que não seja uma classe derivada de str para o método .request() acima eu não vou poder.

    Eu removeria todas as verificações de isinstance() e deixaria o código assim:

    def request(self, url, method, body=None, headers=None):
      if method not in HTTP_METHODS:
        raise TypeError(f'Bolacha.request, parameter method must be a valid HTTP method. Got {method}. {RFC_LOCATION}')
      # ... continua

    Mais adiante nesse código vemos o uso de uma função chamada is_file() que é implementada da seguinte forma:

    def is_file(obj):
      return hasattr(obj, 'read') and callable(obj.read)

    Mais uma vez, nada de errado. Mas também não é muito o meu estilo. No meu estilo essa função sequer existiria porque, mais adiante, quando fosse necessário usar obj que, no código em questão, pode ser uma string ou um file object, eu faria algo assim:

    try:
      lines.extend(encode_file(obj))
    except AttributeError:
      lines.extend(['...'])

    Mais uma vez eu quero deixar claro que é só uma questão de diferença de estilo e que eu usei o código do Bolacha somente para ilustrar essa diferença. Dentro do estilo do Gabriel o código está perfeito (tá, não existe código perfeito, mas o dele tá muito bom).

    Como leitura complementar sobre essas diferenças eu recomendo o artigo isinstance() considered harmful, Duck Typing, Permission and Forgiveness e PEP-3119 – Introducing Abstract Base Classes (funcionalidade de suporte ao estilo usado no Bolacha).

    Por último, ao meu estilo, gostaria de pedir desculpas ao Gabriel Falcão por ter usado o código do Bolacha para ilustrar esse artigo sem permissão. 🙂

  • Dicas para um bom programa em Python

    Dicas para um bom programa em Python

    Oi pessoal, desta vez eu vou pular as ‘desculpas’ por ter demorado tanto para postar aqui no blog e vamos direto ao assunto.

    Recentemente eu tenho trabalhado bastante com Python (dã!) desenvolvendo projetos de diversos tipos e resolvi escrever aqui sobre algumas coisas que pratico enquanto desenvolvo.

    Esse artigo é uma espécie resumo com boas práticas de programação Python que utilizo no meu dia-a-dia.

    Código mais robusto

    Deu certo ou errado?

    O que você faz quando acontece algo errado na execução do seu método? O que você responde à requisição que lhe foi feita?

    Eu tenho visto em muito código por aí os desenvolvedores retornando valores sentinela (None, null, 0, -1, etc.) para avisar que algo incorreto aconteceu na execução do método.

    def f(arg):
       if not arg:
          return None
       return ["resultado", "de", "um", "processamento"]

    Algumas linguagens de programação não possuem estruturas de tratamento de exceção e, neste caso, o uso de sentinelas é válido. Mas quando a linguagem de programação te disponibiliza essa funcionalidade é bom usá-la.

    def f(arg):
       if not arg:
          raise ValueError("Argumento Invalido")
       return ["resultado", "de", "um", "processamento"]

    Deixem as exceções fluirem.

    Isso mesmo. A menos que você saiba exatamente o que você deve fazer quando uma exceção aparece deixe-a exceção “subir”. Pode ser que “lá em cima” alguém saiba cuidar dela adequadamente.

    Quando não fazemos isso estamos ocultando informação importante para os usuários do nosso código (sejam eles usuários, outros desenvolvedores ou nós mesmos).

    def f():
       try:
          return conecta()
       except ExcecaoQueDeveriaSerErro:
          return None

    Quando eu implemento esse tipo de método/função eu faço assim (na verdade eu não implementaria f() e chamaria conecta()):

    def f():
       return conecta()

    O que seu método/função retorna?

    Código que eu encontrei recentemente:

    def get_fulanos():
       q = Q("select * from patuleia where alcunha like 'fulano%'")
       ret = [str(fulano['nome']) for fulano in q]
       if len(ret) == 1:
          return ret[0]
       return ret

    Perceberam o que está errado? O seu método retorna uma lista de Fulanos ou retorna Fulano?

    Isso está conceitualmente errado e pode fazer você perder horas preciosas do seu dia tentando achar um bug causado por esse tipo de código.

    Aconteceu comigo. Note que str() implementa uma interface de sequence da mesma forma que list(). Então o erro passa silenciosamente no caso abaixo:

    old_fulanos = ["Ze Ruela", "Ze Mane"]
    old_fulanos.extend(get_fulanos())
    print(old_fulanos)

    Rodando esse código você vai obter ['Ze Ruela', 'Ze Mane', 'q', 'u', 'a', 'c', 'k'] sendo que, em mais de 90% dos casos, o que você gostaria de ter seria: ['Ze Ruela', 'Ze Mane', 'quack'].

    “Nada” é diferente de “alguma coisa”.

    Essa dica é só uma complementação da primeira e da segunda dica.

    Quando o seu método/função retorna uma collection (seqüência, conjunto, hash, etc) vazia você deve retorná-la vazia e não um valor sentinela (como None). Isso facilita a vida de quem vai usar o seu método/função:

    def vazio():
       return []
    
    for elemento in vazio():
       pass #... faz algo se o conjunto contiver dados ...

    Se você retorna um valor sentinela:

    def vazio():
       return None
    
    elementos = vazio()
    if elementos:
       for elemento in elementos:
          pass # ...

    Notou que tivemos que criar uma variável com o resultado da operação (para não precisar chamá-la duas vezes) e tratar a sentinela com um “if“? Se eu esqueço de tratar a sentinela meu programa vai quebrar.

    Lembre-se sempre que uma collection vazia tem valor booleano “False“.

    Todo ‘elif‘ tem um irmão ‘else‘.

    Sempre que você precisar usar uma construção if/elif coloque uma cláusula ‘else‘.

    Além de usar a cláusula ‘else‘ eu geralmente faço com que ela gere uma exceção. Desta forma eu sou obrigado a trabalhar todas as possibilidades nos ‘if/elif‘ evitando ocultar uma situação que pode ser inválida.

    class InvalidCommand(Exception):
       pass
    
    def minihelp(comando):
       if comando == "print":
          return ("Imprime dados na tela. "
                  "Deixará de ser comando "
                  "no Python 3.0")
       elif comando == "assert":
          return ("Certifica se uma condição é "
                  "verdadeira e gera uma excessão "
                  "em caso contrário")
       elif comando == "...":
          pass # ...
       else:
          raise InvalidCommand(f"Comando {comando} inválido.")

    Eu gosto dessa prática mas isso não significa que você deva seguí-la sempre. Existem situações onde ter um “valor default” é necessário e nestes casos o uso do else sem levantar exceção se faz necessário.

    if comando == "if":
       print("Vai usar elif?")
    elif comando == "elif":
       print("Muito bem. Agora falta o else")
    else:
       print("Pronto. Agora está bom.")

    “Pythonismos”

    Use mais atributos públicos do que atributos protegidos (“_“).

    Programadores acostumados com Java utilizam muito as cláusulas ‘private‘ e ‘protected‘ para encapsular os atributos de um objeto para logo depois implementarem os getters e setters para acessar esses atributos.

    Essa prática é aconselhada em Java porque em algum momento do futuro você, talvez, precise validar esses dados ou retornar valores calculados. Nestes casos os programadores apenas implementam essa lógica nos métodos que acessam o atributo privado.

    Mas em Python isso não é necessário. Em Python você pode transformar seu atributo público em uma “property” que não muda a forma de se acessar o atributo e permite o acrescimo de lógica ao acesso do mesmo.

    Evite usar “__“.

    Por convenção, em Python, todo método ou atributo iniciado com um “_” é considerado privado (equivalente ao protected em Java) e não deve ser acessado de fora da classe mesmo sendo possível fazê-lo.

    Dito isso parece meio óbvio que não precisamos usar “__” para dificultar ainda mais o acesso à esse atributo/método. Além disso o uso do “__” traz alguns incoveninentes para quem quer derivar a sua classe e acessar este método/atributo já que o Python o renomeia acrescentando o nome da classe ao seu nome (__attr vira _Classe__attr).

    Não sobrescreva builtins.

    Python disponibiliza várias funções e classes builtins que facilitam muito o uso da linguagem e economizam digitação. Mas esses builtins tem nomes muito “comuns” e frequentemente a gente usa os nomes dos builtins como nomes de identificadores. Eu mesmo vivo (vivia) fazendo isso.

    O problema é que em certos momentos alguns problemas podem acontecer quando você tenta chamar um buitin que já não é mais um builtin. Na maioria das vezes o problema “explode” logo e você rapidamente conserta mas em alguns casos você pode perder muitas horas tentando achá-lo.

    Algumas pessoas usam um “_” no fim do nome do identificador (ex. “id” vira “id_”) mas eu acho isso um pouco feio então uso só quando não encontro uma alternativa melhor.

    Vou colocar aqui uma tabela de equivalências que eu costumo usar para substituir o nome dos builtins mais comumente sobrescritos:

    • id – ident, key
    • type – kind, format
    • object – obj
    • list – plural (lista de element vira elements)
    • file – fd, file_handler
    • dict – dic, hashmap
    • str – text, msg

    Análise estática economiza seu tempo.

    Eu uso o pylint, mas conheço algumas pessoas que preferem o pyflakes ou o PyChecker.

    UPDATE (2023-08-08): utilizo o utilitário ruff com muito sucesso nos meus projetos mais novos. O ruff agrega todos os utilitários acima e mais vários outros.

    A dica é essa: usar um programinha de análise estática como esses pode diminuir consideravelmente aqueles errinhos chatos de sintaxe, ou de digitação. Pode limpar os ‘import’ desnecessários do seu software, etc, etc.

    É lógico que esse tipo de ferramenta não substitui uma boa política de testes mas é um bom complemento para ela.

    Challenge yourself

    Máximo de 3 níveis de indentação. (ou 4 se estiver dentro de uma classe)

    Ao se esforçar para que seu código não fique muito aninhado você está trabalhando melhor a implementação dos seus métodos e funções. Nem sempre é possível (ou aconselhável) restringir tanto o nível de identação do seu código mas muitas vezes isso melhora a sua implementação.

    Máximo de 2 indireções.

    Recebeu um objeto como parâmetro? Chame apenas métodos dele e evite ao máximo chamar métodos do retorno desses objetos:

    def f(obj):
        obj.metodo() # legal!
        obj.metodo().outro_metodo() # ruim!

    Quando você chama um método pra um objeto retornado por outro método você está aumentando o acoplamento entre as classes envolvidas impedindo que uma delas seja substituída (ou reimplementada) ‘impunemente’.

    Essa regrinha é uma das regrinhas da Lei de Demeter.

    Máximo de 0 ‘if/elif/else‘s.

    Polimorfismo é isso. No mundo OO ideal, perfeito e utópico praticamente não precisaríamos do comando “if” e usaríamos somente polimorfismo. Mas… como não conseguimos isso tão facilmente* devemos, ao menos, usar o “if” com moderação.

    Conclusão

    Esta é uma lista incompleta de dicas para programadores Python. Se futuramente eu lembrar ou aprender algo novo eu volto aqui para falar sobre o assunto.

    Alguns desenvolvedores podem não concordar com as dicas. Neste caso eles podem enriquecer ainda mais esse texto argumentando sobre o as suas restrições no espaço para comentários.

    Se você tiver alguma dica para compartilhar com a gente coloque nos comentários. Se ela for boa mesmo eu coloco ela no corpo principal do blog.

    * eu mesmo só consegui fazer uma aplicação OO funcional sem usar um único if. Era uma implementação do joguinho de adivinhação de animais (aquele que pergunta “Vive na água? (s/n)”) em Smalltalk.

    Update: pequena correção sugerida pelo Francisco nos comentários: s/Classe/_Classe/.

  • “Cagadas” homéricas (ou YA-meme?)

    “Cagadas” homéricas (ou YA-meme?)

    Durante toda a minha carreira “computeira” eu cometi alguns erros absurdos (cagadas?) que eu gostaria de compartilhar com vocês neste post.

    Vou listá-las aqui na esperança de que outros façam o mesmo e que, com isso, o meu sentimento de culpa por tamanhas “obras” fique menor.

    killall foobar

    Certo dia, quando trabalhava na GVT, eu estava numa sessão telnet em um servidor de produção PA-RISC que rodava um HP-UX. Estava logado como root e precisava matar alguns processos que estavam prejudicando o funcionamento da máquina (consumindo CPU, memória, load alto, etc).

    Meu conhecimento de Unix até aquele momento era de ter mexido muito com Linux ao ponto de saber usar com maestria o comando killall. Foi então o que eu usei para matar tais processos.

    Assim que eu apertei “enter” surgiu uma mensagem de que o servidor iria se desligar em X segundos. Entrei em pânico.

    Pois bem… apesar de ser um ‘fera de killall’ eu nunca tinha lido o trecho da manpage deste comando que dizia (no Linux):

    Be warned that typing killall name may not have
    the desired effect on non-Linux systems, especially
    when done by a privileged user.
    

    O que aconteceu? O killall do HP-UX envia um sinal para todos os processos da máquina e é usado pelo comando shutdown para matar todos os processos em execução.

    Foi assim que eu desliguei, pela primeira vez, um servidor em produção de uma grande empresa de telefonia praticamente parei as vendas da empresa por pelo menos 1 hora (uma máquina dessas leva cerca de 20 minutos para voltar pro ar + acertos de configuração perdidos por causa do reboot).

    delete from tabelao;

    Mais uma vez na GVT… coitados…

    Eu estava desenvolvendo um programinha que fazia acesso ao banco de dados Oracle e para isso eu constantemente testava algumas queries, inserts e deletes nas tabelas de um banco de dados que ficava no ambiente de desenvolvimento da empresa.

    Nos bancos de dados do ambiente de desenvolvimento a gente encontrava um subconjunto dos dados do ambiente de produção e podíamos manipulá-los com tranquilidade porque uma vez por dia/semana as bases eram repopuladas com dados oriundos do ambiente de produção.

    Em certo momento eu precisava deletar todos os registros de uma tabela (cerca de 50 registros) da base de dados do ambiente de desenvolvimento e rodei um:

    delete from tabela;

    Fiz os testes que precisava fazer sem rodar um commit e no final fiz um rollback.

    Neste intervalo de tempo ocorreu um problema que exigiu minha atenção em um dos servidores de produção e esse servidor era justamente o servidor ‘equivalente’ ao de desenvolvimento onde eu estava trabalhando. Resolvi o problema do servidor rapidamente e voltei para o desenvolvimento.

    Aí é que está o problema… o meu cérebro trocou de servidor mas meus dedos não e no fim eu rodei um delete from tabela; no servidor de produção da empresa 😀

    O estrago teria sido pequeno se tal tabela não tivesse centenas de milhares de registros fazendo com que o Oracle praticamente parasse de responder à requisições até que ocorresse uma falha de “estouro de segmento de rollback” (falha que demorou pra acontecer porque as configurações do Oracle eram muito ‘generosas’).

    Seria muito difícil acontecer do estrago ser grande. Para que isso ocorresse a falha que ocorreu não poderia acontecer e depois do delete eu ainda precisaria dar um commit.

    Se isso tivesse acontecido eu teria mudado de profissão 🙂

    /home/osvaldo/icons

    Essa foi no meu “início de carreira” com Linux. Eu trabalhava na Conectiva (atual Mandriva) e fiquei responsável por criar um sistema de “temas” para o Conectiva Linux 5.

    Os temas deveriam funcionar igualmente no Gnome, KDE e no WindowMaker que, até então, era o WM que eu usava.

    Fiz os pacotes, scripts, configurações e enviei para a máquina de integração (mapi). Dei essa tarefa como terminada e passei para as próximas.

    Lançado o Conectiva 5 o sistema “explodiu” nas máquinas dos usuários e o sistema de temas não funcionava como deveria em todas as máquinas que usavam o WindowMaker .

    Ao analisar o problema vi que no arquivo de configuração do WindowMaker tinha um “/home/osvaldo” no caminho de busca de imagens e ícones que causava a ‘quebra’.

    Resultado: as coisas funcionavam perfeitamente na minha máquina e em todas as outras máquinas do mundo que tivessem um usuário “osvaldo”.

    Esse foi um bug entre os vários desse sistema de temas que criei e que futuramente (por razões óbvias) foi abandonado.

    Se você ainda tem um Conectiva 5 para instalar por aí faça o teste. 😀

    Off-topic

    Alguns, talvez, ainda não saibam mas eu vivi um período em que queria largar da área de informática e virar publicitário (ok, eu era jovem).

    Neste período “publicitário” da minha vida eu trabalhei em uma agência de propaganda chamada DLMRozani e lá nós fazíamos toda a mídia local das várias Lojas Americanas do país (a conta nacional era da agência Talent).

    A mídia local inclui anúncios televisivos (para as afiliadas locais), anúncios em jornais locais e aqueles folhetinhos de ofertas que todos já devem ter visto.

    Num desses folhetos onde anunciamos carne de 1ª à R$3,90/kg quando o preço correto era algo como R$13,90/kg (não lembro detalhes da oferta).

    A sorte que era uma dessas “ofertas relâmpago” (válidas por 1 hora), mas o fato é que vendemos 400kg de carne em 1 hora. Filas se formaram em volta do quarteirão das Lojas Americanas e caminhões não paravam de chegar trazendo carne para ser vendida (aquele blablabla de “enquanto durarem os estoques” não tem muito amparo legal pelo código de consumidor).

    Só para contextualizar: nesta época as Lojas Americanas tinham lojas que funcionavam como supermercado em várias cidades do Brasil. Essa operação foi vendida posteriormente para um grupo francês chamado Stoc.

    Finalizando

    Se eu lembrar de mais algumas “cagadas” eu volto a atualizar esse post mas acho que essas foram as maiores.

    E vocês? Fizeram alguma dessas já?

  • I Has Arduino!

    I Has Arduino!

    Hoje de manhã chegou uma encomenda pelo correio que eu estava esperando a muito tempo: meu Arduino 🙂 Quando eu tinha uns 9/10 anos de idade eu adorava ‘brincar com eletrônica’ com um amigo meu que tinha uma oficina em seu quintal. Todo dia depois da aula a gente jogava Atari na minha casa e depois ia para o “laboratório” dele montar os projetos que saiam nas revistas Experiências e Brincadeiras com Eletrônica Júnior ou na Be-a-Bá da Eletrônica.

    Certo dia esse amigo meu me chamou na casa dele porque ele tinha acabado de ganhar um computador. Era um MSX Expert 1.1 da Gradiente completamente sem acessórios. Quando ele ligou o computador e começaram os primeiros acordes do cartucho de demonstração (Ligue-se ao Expert) eu pensei: “É isso o que eu quero pra minha vida”.

    Era exatamente um desses

    O processo de programação na época era +/- assim:

    10 CLS
    20 ON ERROR GOTO 70
    30 PRINT "DESLIGANDO O ATARI DA TV E LIGANDO O EXPERT"
    40 PRINT "COPIANDO PROGRAMA DO LIVRO..."
    50 PRINT "EXECUTANDO O PROGRAMA (RUN)"
    60 PRINT "PARABENS! DELIGUE O COMPUTADOR E PERCA TUDO"
    70 END
    80 PRINT "CORRIGE OS ERROS"
    90 GOTO 50
    RUN
    

    Mas não foi pra falar disso que eu criei esse post. Vamos voltar ao assunto.

    Enquanto morava em Recife o Elvis Pfützenreuter me deu alguns componentes eletrônicos que ele tinha comprado para usar em uma maquete de ferromodelismo que ele tinha desistido de continuar. Isso me fez lembrar de como era bom o cheiro de solda e decidi retomar a eletrônica como Hobby.

    Ainda estou aprendendo!

    Assim como na computação as coisas evoluiram nos últimos anos com a eletrônica também. Então “aquela” eletrônica que eu conhecia onde a gente usava só uns transistores, uns resistores, uns capacitores, etc… se transformou em algo muito parecido com… informática!

    “Brincar” com eletrônica nos dias de hoje quase sempre te levará a usar um microcontrolador, ou seja, você terá um chip programável com software para trabalhar.

    E é aí que o Arduino entra na história.

    O Arduino é um hardware com especificação livre e possui várias implementações diferentes mas todas elas possuem um microcontrolador Atmel instalado. Como o projeto é aberto existem diversas extensões e projetos que usam ele tal no universo do software livre.

    A idéia do hardware livre é tão semelhante à do software livre que existem comunidades formadas em torno destes projetos. As idéias se intercalam também. Para ver isso basta olhar para a IDE utilizada para programar o Arduino. Usa o GCC como compilador e a IDE tem uma implementação livre feita em Java.

    Se você, como eu, tem interesse nesse universo e quiser adquirir uma placa Arduino pra ‘brincar’ é só fazer uma visita no site da Symphony e comprar um. O modelo que eu tenho aqui é o de 16K:

    I Haz Ardooino!

    Update: Esqueci de agradecer ao Blog do Jê que é um dos “praticantes de Arduino” no Brasil e notíciou o lançamento da placa pela Symphony (que tornou a compra mais $acessível$)

  • Cross-compiling fácil fácil

    Como eu já contei no post anterior no meu novo trabalho a gente tem que lidar com cross-compiling (compilação cruzada) o tempo todo. A idéia da compilação cruzada é simples: você compila um programa P em uma plataforma A e o binário produzido deverá rodar em uma plataforma B.

    O conceito é simples, o seu funcionamento na teoria também. Para compilar um típico programa Linux em um computador x86 para rodar na plataforma ARM bastaria ter o toolchain, que é o conjunto de ferramentas que engloba o binutils (onde fica o linker) o gcc (onde fica o compilador C/C++) e em algumas bibliotecas básicas já como binários ARM (a libc é uma delas).

    O problema do ovo e da galinha dificulta um pouco a construção de um toolchain (você precisa compilar o compilador) mas não é incomum que esses toolchains já sejam distribuídos com a plataforma ‘alvo’, logo, esse problema não é muito grande.

    Com cross-toolchain já instalado a teoria diz que bastariam os seguintes comandos (assumindo que a nossa plataforma alvo seja ARM) para compilar um programa:

    $ ./configure --host=arm-linux
    $ make
    $ make install

    Com algumas pequenas variações disso conseguiríamos fazer a compilação cruzada de ‘todo o Linux’, mas na prática a teoria não funciona… 🙂

    O que acontece é que um grande percentual das aplicações (regra e não exceção) simplesmente ignora o fato de que no futuro elas serão submetidas à compilação cruzada e simplesmente não funcionam nessas circunstâncias.

    O Python é um desses programas. O interpretador compila perfeitamente, mas as extensões em C da biblioteca padrão não. O problema é que o Python usa o módulo distutils para executar tal tarefa e o mesmo é feito em Python. Neste caso precisamos de um Python ARM para executar a segunda fase do processo de build. Como executar um Python ARM em uma máquina x86?

    Uma das maneiras é compilar primeiro um interpretador Python x86, renomeá-lo para algo como ‘hostpython‘, depois compilar um interpretador Python para ARM e aplicar uns patches no Makefile.in do Python para que ele chame o ‘hostpython‘ para compilar as extensões C. Mas os efeitos colaterais dessa solução são enormes porque existem extensões que usam bibliotecas do sistema (OpenSSL, Socket, SQLite, …) e o distutils não irá procurá-las no lugar correto pois não sabe o que é compilação cruzada.

    Aí então entra uma técnica de transparência de CPU que aprendi a fazer com a turma do projeto Scratchbox (aperfeiçoada pela turma do projeto Mamona) que é bem simples e permite fazer compilação cruzada sem modificar nada nas aplicações que estão sendo compiladas.

    A idéia é usar o binfmt do Linux para dizer que todos os binários ARM deverão ser executados pelo qemu (pegadinha 1: esse qemu deve ser estático e estar instalado dentro do chroot no path definido nas configurações do binfmt) e criar um ambiente chroot com tais binários.

    Dentro desse ambiente todos os binários ARM rodam com o qemu e todos os binários x86 rodam nativamente na sua plataforma (assumindo que ela é x86) sem que você sequer note a diferença entre o funcionamento deles. Desta forma podemos colocar então o nosso cross-toolchain dentro desse chroot fingindo ser um toolchain nativo ARM (tem uma pegadinha aqui: esse toolchain precisa ser estático e não dinâmico pois as bibliotecas nesse nosso ambiente são ARM e não mais x86).

    Você está me perguntando “porque não compilar um toolchain nativo pra rodar dentro desse chroot“? Só por questão de velocidade. O gcc rodaria muito devagar sendo emulado pelo qemu.

    Agora é só sair compilando os programas normalmente. Mesmo aqueles que não estão preparados para compilação cruzada:

    $ ./configure
    $ make
    $ make install

    Neste exato momento estou compilando o SQLite3 (após ter terminado o OpenSSL) dentro do ambiente chroot com binários ARM (XScale) que rodam emulados pelo qemu. Tudo isso para que no final eu tenha um Python com Pygame 100% funcional.

    Ok. Agora é a hora das notícias ruins:

    • O qemu não emula 100% das syscalls, logo, você poderá esbarrar com uma das famigeradas mensagens “Unsuported syscall XX“. Nestes casos verifique se já não existe um patch que implementa o suporte à essa syscall no qemu e recompile-o (lembrando que o qemu precisa ser estático).
    • O qemu não lida muito bem com threads, logo, se os scripts de build do seu programa testam threads eles podem quebrar (ou bloquear). Neste caso a sugestão é: retire esses testes dos scripts e assuma que sua plataforma tem sim (ou não) suporte à threads.
    • Lembre-se sempre que o kernel não é emulado pelo qemu! Se o seu programa usa ferramentas como o uname, por exemplo, ele irá retornar informações da sua plataforma nativa e não da plataforma emulada. Desenvolvimento para o kernel também não é muito viável.
    • Pode acontecer da tua aplicação quebrar ‘silenciosamente’. Lembre-se que pode ser o qemu quebrando e ocultando a real causa do problema. Nesses casos utilize as funções de depuração do qemu.
    • Lembre-se de ter o /proc e o /sys montados dentro do seu chroot. Alguns programas usam as informações disponíveis nesse lugar para a sua construção. Lembre-se também que esse /proc e /sys são da sua plataforma nativa e não da plataforma emulada, logo, eles poderão fornecer informações incorretas.
    • Dica: tenha a sua área de trabalho ($HOME) montada via mount --bind dentro de seu chroot para que você possa ter vários chroots compartilhando o mesmo $HOME.
  • Ambiente Isolado para Python com virtualenv

    Ambiente Isolado para Python com virtualenv

    Boa parte do meu dia-a-dia de desenvolvedor é gasto em proramando em Python. Gosto de estar sempre atualizado com o que há de novo para essa linguagem e para isso saio instalando tudo o que aparece para para experimentar. Além de Python o Linux também faz parte da minha vida e uso ele quase 100% do meu tempo (em vias de mudar para o OS X).

    O conteúdo deste artigo está desatualizado e ele é mantido aqui apenas por motivos históricos.

    A plataforma Python, de uns tempos pra cá, vêm padronizando os arquivos Eggs para distribuição de aplicações e bibliotecas. Em conjunto com o PyPI (Python Package Index) e o utilitário easy_install (que é parte do framework setuptools) é possível instalar componentes Python com apenas um comando.

    A facilidade para instalar esses pacotes é enorme mas removê-los é chato porque envolve a edição de alguns arquivos texto, e ter permissão de escrita no diretório de bibliotecas do Python (permissão que também é necessária para a instalar o pacote).

    Cada pacote instalado acrescenta uma entrada ao sys.path do Python fazendo com que o tempo para importar um módulo aumente um pouco mais (cada uma dessas entradas é consultada em busca do módulo e se você der uma olhada na saída do strace verá que a procura por um módulo envolve vários passos).

    O Linux que eu uso (Ubuntu) precisa ter um ambiente Python estável, já que grande parte de suas aplicações roda em cima dessa linguagem, ou seja, danificar esse ambiente pode atrapalhar todo o funcionamento do sistema.

    Isso tudo junto com o fato de que adoro experimentar as novidades do mundo Python faziam com que meu Python ficasse totalmente poluído com versões bleeding edge de bibliotecas que muitas vezes são incompatíveis com as versões “oficialmente suportadas” pelo pessoal que faz o Ubuntu.

    Seria necessário um jeito fácil de se criar ambientes isolados do Python usando como base a própria instalação do sistema para que eu pudesse fazer esses testes e experiências sem danificá-lo. Não ficar replicando cópias de Python pela máquina também seria interessante.

    E então surge a solução…

    Parece engraçado mas no mesmo dia que perdi horas “arrumando” o Python em meu computador eu li no blog do Ian Bicking que ele tinha desenvolvido um programinha que fazia exatamente o que eu precisava: o virtualenv.

    O uso do virtualenv é extremamente simples e direto. Basta instalar, executar e ativar.

    Instalação

    Se você está usando Ubuntu ou Debian:

    sudo apt-get install python-setuptools
    sudo easy_install virtualenv

    Se não está:

    wget http://peak.telecommunity.com/dist/ez_setup.py
    sudo python ez_setup.py

    Criando o ambiente

    Para criar um ambiente basta executar o virtualenv e passar como parâmetro o nome do diretório onde tal ambiente será instalado:

    virtualenv meu_python

    Esse comando irá criar um diretório chamado meu_python com os diretórios:

    • bin – executável do interpretador, o script easy_install e o arquivo activate que será usado para “ativar” o ambiente. Quando o ambiente está “ativo” os executáveis dos aplicativos Python são instalados aqui também.
    • lib – a árvore com links simbólicos e/ou cópias de todos os módulos e bibliotecas do Python. Quando esse ambiente está “ativo” os módulos e pacotes serão sempre instalados dentro desse diretório.
    • include – dentro desse diretório estão os links simbólicos para todos os headers do Python que são necessários para se compilar extensões escritas em C para ele.

    Ativando o ambiente para usar

    Para usar esse ambiente recém-criado é necessário ativá-lo. para isso basta executar o seguinte comando:

    source meu_python/bin/activate

    Esse comando irá adicionar o diretório meu_python/bin no PATH da sua sessão e mudar o prompt para que você possa distinguir visualmente quando este ambiente está ativo.

    Atenção: O virtualenv não cria o link simbólico python -> python2.5, portanto, se precisar dele você terá que criá-lo à mão com o seguinte comando

    (cd meu_python/bin; ln -s python2.5 python; hash -r)

    Depois disso é só sair instalando as coisas sem a menor preocupação.

    PS. Eu não testei o virtualenv no Windows nem no Mac OS X mas no site do projeto é possível notar que tem gente usando ele também nessas duas plataformas, portanto, eu acho que ele também funcione corretamente nelas.

  • $oftware Livr€

    Pra não dar muito trabalho pra escrever esse artigo não vou ficar fazendo links para a discussão que se iniciou com um post sobre o modelos de negócios com SL. Para quem quiser mais detalhes sigam os links do br-linux.org.

    Esse artigo é antigo e já não reflete a minha opinião pessoal. Mantive aqui apenas para registro histórico.

    A referência que fiz ao meu apelido ‘xiita’ induz o leitor a entender que isso é sinônimo de ‘radical’. O tempo e o estudo me mostraram que nada poderia estar mais longe da verdade. Xiitas são uma vertente do islamismo e, como tal, prega a tolerância e a paz.

    Osvaldo

    O Software Livre apareceu na minha vida a partir do ano 2000 quando fui contratado pela Conectiva S/A (Mandriva) para integrar a equipe de P&D e trabalhar no desenvolvimento do Conectiva Linux. Poderia dizer que eu já mexia com isso antes mas estaria exagerando já que o máximo que eu fazia era disponibilizar os códigos fonte de meus softwares (feitos em Clipper Summer’87 :)) para meus clientes. São só 8 anos mas tempo o suficiente para entender e ver muitas coisas acontecerem.

    De 2000 pra cá passei por muitas empreitadas e passei por lugares onde recebi apelido de ‘xiita’ (por causa do entusiasmo pelo SL) e sou tratado como ‘traidor do movimento’ quando digo que sonho em ter um Apple rodando OS X.

    Fazendo uma retrospectiva por todos esses 8 anos eu posso perceber uma certa coerência entre meus ideais e minhas atitudes. Evidentemente algumas idéias mudaram e outras atitudes também mas a essência permaneceu a mesma: Eu gosto de software bom.

    Ser livre ou proprietário é só um dos critérios (importantes) que uso para avaliar a qualidade de um software. Se eu gosto de um software proprietário e acho que o valor cobrado por ele é correto eu certamente pagarei. Já comprei licenças de software para meu antigo Palm, licença para o Nero Burning ROM, anti-vírus NOD32, e até mesmo a do Windows OEM que veio com meu Notebook (nesse caso eu não acho o software bom, mas ele era necessário para rodar o Nero :))… mas jamais comprarei uma licença do Microsoft Office (detalhe: sou fanático por planilhas eletrônicas desde o 1-2-3 e acho o Excel a melhor planilha que existe atualmente).

    Eu também tenho alguns softwares piratas rodando na máquina com Windows (principalmente a alternativa ao GIMP :P) e isso é algo que me deixa desconfortável pois sou daqueles radicais que acham que pirataria é contravenção. Prometo adquirir esse software assim que surgir uma promoção para estudante (ele é muito caro mas vale o preço).

    O critério do software bom serve para definir minhas escolhas no uso de um software mas faço toda a força do mundo para que os softwares que desenvolvo sejam Livres ou, no mínimo, OpenSource. Mesmo que seja um software desenvolvido no trabalho. Meu histórico de desenvolvimento de sofware livre é pequeno e nenhum deles “emplacou” mas isso não fez com que eu deixasse de acreditar no modelo pois, afinal, eu os desenvolvo principalmente para os meus propósitos (ou para os propósitos da empresa onde trabalho). Se outros acharem útil e quiserem colaborar ótimo mas caso contrário está bom também.

    É muito difícil ganhar dinheiro desenvolvendo e vendendo software livre e quando esse dinheiro começa a entrar nunca estará na mesma proporção da entrada financeira de um software proprietário. Mas ao tentar ganhar a vida “vendendo” software o autor precisará muito mais do que código para se sustentar. Ele precisará de muita criatividade, muita paciência, um bom planejamento, uma pitada de sorte e uma visão realista do mundo.

    Idéias não faltam para que isso dê certo e muitas empresas espalhadas pelo mundo já comprovam isso, mas no Brasil a coisa ainda é um pouco mais complexa pois uma série de fatores como educação deficiente, preguiça, ‘malandragem/jeitinho’, ‘lei de Gerson’ e muito discurso dificultam o desenvolvimento de um modelo baseado no SL aqui no Brasil.

    O próprio autor do manifesto citado no artigo do br-linux.org era um dos que passavam horas escrevendo seus “e-mails-discursos” ao invés de trabalhar no desenvolvimento de seu software. O software que ele desenvolveu também poderia ter sido feito em forma de colaboração para outros projetos (plugin pro Webmin?) mas não foi. Como um desenvolvedor pode querer colaboração para seu projeto se nem ele foi capaz de (ou se interessou em) colaborar com outro projeto?

    Com relação à contribuição e à comunidade brasileira de SL: esqueça. A comunidade brasileira de desenvolvedores de SL é quase uma obra de ficção. O brasileiro quer “parasitar” o software livre. Ele quer usar o software “de graça” e não fazê-lo ou melhorá-lo. É evidente que temos grandes excessões mas elas servem apenas para confirmar a regra.

    Os brasileiros acham que usar SL e ficar repetindo as palavras do Stallman bastam, mas esquecem que software é feito de código e que sem ele nada vai existir ou melhorar.

    Portanto os meus conselhos para quem quiser se envolver com o desenvolvimento de SL são:

    1. colabore com algo que já exista!
    2. se não existir ou não for possível colaborar: faça!
    3. faça mais!
    4. escreva, documente e desenvolva em inglês. Não use a língua portuguesa para não limitar os seus colaboradores a 0.
    5. lance o software (divulgue-o em todos os lugares possíveis).
    6. use-o.
    7. não espere nada em troca.
    8. não espere nada em troca mesmo (principalmente de brasileiros).

    Acredite, você será recompensado.

    Update: Eu reli o meu texto depois de publicá-lo e percebi que, apesar de ter citado a existência de desenvolvedores brasileiros que colaboram com SL, eu não dei a ênfase necessária (e até peguei um pouco pesado demais). Pois bem, existem desenvolvedores brasileiros de SL e a quantidade deles vêm aumentando recentemente mas navegando por sites especializados como o ohloh.net ou o sf.net é possível ver que ainda falta muito pra gente ser notado no meio das comunidades de SL.