Blog

  • Arqueologia: eu e Tron

    Arqueologia: eu e Tron

    Quando eu assisti o filme Tron pela primeira vez eu delirei. Imaginem que eu era uma criança com 5 ou 6 anos. (Explico: Nasci em 77, o filme foi lançado em 82. Assisti o filme na TV, logo, um ou dois anos depois do lançamento do mesmo nos cinemas).

    Nessa época eu sequer tinha ganhado meu Atari (primeiro ‘computador’ que eu teria) e o mais próximo que tinha chegado de um videogame foi num fliperama onde meu pai havia me levado (e de uma vaga lembrança de um Telejogo Philco/Ford que um tio rico tinha).

    Sim, isso funcionava e era até divertido

    Mesmo assim… O filme me marcou tão profundamente (ui!) que, tempos depois, quando eu tive acesso ao meu primeiro computador (um MSX Expert da Gradiente que tenho funcionando até hoje) a primeira coisa que fiz foi tentar programar o jogo das ‘bikes’ pra ele.

    Expert igualzinho ao que eu tenho

    Mas tinha um problema… eu não sabia programar nada! Então meu pai me matriculou numa escolinha que ensinava programação (LOGO e Basic Apple). Eu chegava da aula todo dia e tentava criar o jogo no meu computador. E demorou até eu ter algo que funcionasse.

    Algum tempo depois ganhei um livro onde, em um capítulo, eles implementavam o jogo com o nome “Pedalando”. Fui todo animado ler o código fonte deles pra ‘roubar’ algumas melhorias e tive um sentimento misto de decepção e orgulho ao ver que a implementação deles era inferior à minha (eu tenho esse livro também mas ele está num depósito tão entulhado que deu preguiça de pegar).

    “A edição que eu tenho tem capa azul e uma tartaruga Logo na capa!

    O tempo passou, as fitas cassete onde gravava essas coisas mofaram (literalmente) e eu acabei perdendo essa versão do jogo.

    Mas a minha mania de implementar “Tron” em todos os computadores, sistemas operacionais e linguagens de programação onde tinha acesso não se perdeu.

    Tela inicial do jogo (note o meu dom para o design)

    Entre 1990 e 1994 eu fui desenvolvendo a última versão de Tron que implementei. Não demorei 4 anos pra desenvolvê-la mas ao longo desses 4 anos fui ‘evoluindo’ minhas habilidades como programador e usava esse código para testar várias coisas novas.

    Enfim. Pra quem quiser ver o código fonte é só visitar o repositório dele no Github. Ele foi desenvolvido com Turbo Pascal 6.0 e depois com Turbo Pascal 7.0. Não preciso dizer que o código é velho, não funcionará em nenhum computador que use um sistema operacional ‘moderno’. Se você tiver uma imagem com MS-DOS em algum lugar é provável que as coisas funcionem. Os arquivos com as músicas (no formato .mod) não estavam mais nos meus arquivos, portanto, coloquem qualquer uma ou joguem sem música :D)

    Mas a história não acaba aqui…

    Sempre gostei de música eletrônica e andava de um lado pra outro com discos do Kraftwerk, Bomb the Bass, House & Remix Internacional, etc debaixo do braço.

    Homework (acreditam que me roubaram o CD e só tenho o encarte?)

    Alguns anos depois disso tudo eu tive uma época onde queria ser “DJ” e um amigo meu que curtia um tipo de som mais ‘underground‘ me emprestou um CD chamado Homework de um grupo chamada Daft Punk. Gostei tanto do CD que acabei comprando desse meu amigo (por uma grana alta!).

    E os capacetes tem LEDs que exibem mensagens 😀

    Desde essa época venho acompanhando o trabalho da dupla de franceses que usam capacetes muito malucos. Gostei de muita coisa que eles fizeram. Outras coisas não gostei tanto assim. Mas no geral sou fã do trabalho deles.

    Pretendo assistir a estréia no IMAX 3D! 😀

    Agora vocês imaginam o meu estado de ansiedade pra assistir Tron Legacy.

    Trata-se “apenas” da continuação do filme que mudou a minha vida e que determinou tudo o que eu faria até hoje, com uma trilha sonora de uma dupla de músicos da qual eu sou fã desde a época em que eles eram considerados ‘underground’.

    Filmes como Guerra nas Estrelas, Matrix, Explorers, etc foram muito importantes na minha infância mas Tron fez toda a diferença.

  • E os programadores, onde erram?

    O @marionogueira provocou e eu vou responder.

    Carreira de programador

    Mas antes vou explicar porque eu compartilho da visão de que o trabalho de desenvolvedor guarda semelhanças com o trabalho de um artista (importante dar destaque à palavra “semelhança” para não pensar que as coisas são iguais).

    Como programador você pega uma especificação abstrata que se parece com uma ‘inspiração’ e começa a trabalhar nela até obter algo ‘bruto’, aí você vai lapidando (iterando) sobre esse trabalho, simplificando, e aproximando o software daquela inspiração (especificação).

    Nesse processo o programador vai deixando seu “estilo” no código. Na escolha de algorítmos, de estruturas de dados, nos textos de comentários e até mesmo no “Coding Style”. Essas características são tão notáveis que depois de um tempo é possível identificar o autor do código mesmo quando ele não está ‘assinado’.

    Uma diferença importante entre o trabalho de artistas e de programadores é que raramente vemos o trabalho em equipe (times) no universo das artes e o mesmo é quase uma regra no mundo do software.

    Mas daqui para frente eu vou falar sobre um tipo específico de artistas: aqueles que trabalham sob encomenda pintando retratos de reis e nobres para garantir o seu sustento. E para ilustrar o meu raciocínio vou usar uma das obras de arte mais conhecidas no mundo: a Monalisa.

    A Monalisa (La Gioconda) foi uma obra encomendada por Francesco del Giocondo a Leonardo Da Vinci. Existe muitas versões, mitos e mistérios que cercam essa obra mas vamos nos ater à “história oficial”.

    Francesco passou uma especificação para Da Vinci (ele queria um retrato de sua esposa Lisa Gherardini) e provavelmente especificou prazo e preço. Ou talvez tenham feito um contrato de escopo aberto? Não sei, não estudei essa história muito a fundo.

    Leonardo Da Vinci, então, fez os primeiros esboços e foi apresentando esses esboços ao contratante. Talvez não tenha feito isso porque o contratante confiava plenamente na capacidade de entrega dele. Mas não teria sido vergonha se tivesse que apresentar os esboços no meio da execução do projeto.

    O resultado final foi um trabalho encomendado, executado no prazo combinado, à um custo determinado e que, mesmo assim, era uma obra de arte.

    Curiosamente o escritório da Triveos é vizinho de uma escola de pintura. Todo dia passo em frente à sala de aula e vejo o trabalho dos alunos. É fácil perceber que não tem nenhum Leonardo Da Vinci ali (por enquanto). Falta-lhes experiência. Prática. Com o tempo e empenho eles se transformarão em bons artistas. Talvez gênios.

    O mesmo acontece com os programadores. Só com a prática, a experiência, e com o domínio da técnica um programador se tornará um ‘artista’ de verdade.

    Mas voltando à questão levantada pelo @marionogueira…

    O ‘mundo’ está errado no gerenciamento dos programadores quando eles mudam escopo, prazo e custo dos projetos a todo o instante e ainda exigem uma obra de arte como resultado do trabalho. Ou quando não permitem que o ‘artista’ trabalhe ao seu modo.

    A Monalisa seria uma obra de arte se, durante o seu desenvolvimento, o contratante desse palpites sobre as cores e cenários deveriam ser usados na obra?

    Mas os programadores também erram!

    Erram quando aceitam o desafio de desenvolver uma obra de arte sem ter o domínio adequado da técnica, a prática e a experiência necessária para transformar aquela especificação em arte.

    Em projetos ideais onde o escopo é claro, o prazo é razoável e custo está sob controle os programadores falham quando não compreendem que, mesmo tendo sido encomendada, a ‘obra de arte’ é dele também. Sem essa compreensão eles deixam de se comprometer com sua execução.

    Mesmo tendo sido encomendada, Da Vinci não fez a Monalisa ‘nas coxas’. Ele fez o máximo possível para entregar uma obra de arte única. Isso fica claro em projetos open-source onde a obra e o nome do artista fica público.

    Um artista não precisa de foco e disciplina para se inspirar. Aliás, isso pode até atrapalhar. Mas para executar a obra é necessário muito foco e muita disciplina. Principalmente se a obra foi encomendada e tem custos e prazos pré-estabelecidos.

    O programador falha quando ele não tem foco e disciplina no seu trabalho. Twitter, MSN, GTalk, IRC e outros sugadores de foco, hoje, ficam mais tempo em funcionamento do que a IDEs, editor de textos e outras ferramentas de desenvolvimento. Eu sei. Eu vivo isso.

    Fora isso eles podem errar em outras questões que envolvem relacionamento interpessoal, trabalho em equipe, ética, etc. Mas nessas questões todos podem errar.

  • Como garantir um emprego de desenvolvedor

    Como garantir um emprego de desenvolvedor

    Post rápido e ligeiro com uma lista de atributos que certamente vão garantir a sua vaga como desenvolvedor em qualquer empresa que valha a pena trabalhar.

    Cada atributo tem um dos graus de importância abaixo (do mais importante para o menos importante):

    1. Vital – característica mais do que essencial para vagas de desenvolvedor ou para qualquer outro tipo de posição.
    2. Essencial – característica imprescindível para um desenvolvedor.
    3. Importante – característica importante mas não imprescindível. Pode-se contratar um desenvolvedor que não tenha essa característica desde que haja um compromisso do mesmo em desenvolvê-la.
    4. “Plus” – não faz muita diferença mas pode ser uma característica que pode desempatar (a favor de quem a tem) numa disputa entre dois ou mais candidatos.
    5. Desnecessária – não faz diferença alguma.
    6. Condenável – característica que pode depor contra a sua candidatura.

    O que está escrito aqui é a minha visão sobre o assunto. Algumas empresas contratantes podem divergir no grau de importância de cada atributo. Outras, por questões legais, podem exigir determinada característica listada como:

    • Comunicação (vital) – comunicação escrita e verbal, capacidade de argumentação e de expressar idéias e conceitos.
    • Prazer em programar (vital) – você programa nas horas vagas? Não? Então desista. Corra atrás de trabalhar com aquilo que você faz nas horas vagas. Todos ficarão gratos.
    • Prazer por aprender coisas novas (vital) – Veja… eu disse “prazer por” e não “interesse em”.
    • Inglês para leitura (vital) – não dá mais tempo de esperar por traduções de documentação.
    • Programação (essencial) – tem que saber teoria e prática. Conhecer algoritmos, estruturas de dados, conceitos de OO, paradigmas de programação, teoria da computação, matemática, …
    • Familizarização rápida com ferramentas (essencial) – você é capaz de corrigir um bug numa aplicação escrita numa linguagem que você não conhece em quanto tempo? Consegue produzir código numa linguagem nova em menos de uma semana?
    • Inglês para escrita (essencial) – grande parte dos softwares, bibliotecas e sistemas que usamos hoje são desenvolvidos por estrangeiros. Freqüentemente precisamos trocar um e-mail com esses desenvolvedores.
    • Conhecer bem ao menos uma linguagem (essencial) – essa linguagem varia de acordo com o que você deseja desenvolver, mas ela tem que ser uma espécie de ‘segundo idioma’ seu. No meu caso essa linguagem é Python, mas poderia ser outra.
    • Inglês conversação (importante) – grande parte dos lugares bacanas pra se trabalhar, hoje, são estrangeiros, tem filiais fora do país ou estão contratando estrangeiros pros seus times. Poder conversar com eles é importante.
    • Ter familiaridade com ‘linguagens chave’ (importante) – algumas linguagens de programação estão presentes em tantos lugares que não é mais possível desconhecê-las: assembly de pelo menos 1 plataforma, C, Shell Script, linguagem funcional (fico devendo essa :D), linguagem OO (Java, Smalltalk, Python, Ruby, …).
    • Participação em projetos FLOSS (importante) – universo perfeito para exercitar, experimentar, participar, desenvolver, aperfeiçoar, … todas as características listadas aqui. Alguns lugares onde trabalhei sequer pedia curriculums para contratar um desenvolvedor e usavam só a participação dos mesmos em projetos FLOSS
    • Formação acadêmica (plus) – desde que seja numa boa faculdade (USP, Unesp, UNICAMP, UF*, UTF*, PUC*, …) podem indicar que os alunos aprenderam alguns fundamentos importantes de programação. O convívio social dos alunos para estudo, execução de projetos e trabalhos também acrescenta.
    • Certificações (desnecessária) – empresas que pedem ou avaliam certificações não podem ser empresas onde valha a pena trabalhar. Empresas que usam certificações são aquelas que são incapazes de avaliar corretamente os candidatos e ‘terceirizam’ essa tarefa para as entidades certificadoras. Uma empresa incapaz de avaliar um candidato não pode ser capaz de lhe dar boas condições de trabalho.
    • “Corporacionismo” (condenável) – profissionais que falam “frases que agregam valor e aumentam a sinergia do time junior de colaboradores” ou que acham fundamental a existência de uma regulamentação no mercado de trabalho de TI geralmente são aqueles que não querem ou não conseguem se destacar como desenvolvedor por conta própria e precisa de uma ‘mãozinha’ do governo pra isso.

    Esse artigo descreve algumas características que um desenvolvedor deve ter para conseguir um emprego. Mas se o desenvolvedor quiser empreender e montar o seu próprio negócio, ele precisa das mesmas características? Sim, mas com graus de importância diferentes. Além desses atributos são necessários alguns outros que tentarei abordar em outro artigo.

  • Desafios e Desafio #1

    Sempre gostei de desafios do tipo ‘quebra-babeça’ que podem ser feitos com um lápis/caneta e um pedaço de papel qualquer.

    Conheço uns bem legais, frequentemente encontro alguns desafios novos e vou começar a postá-los aqui pra outras pessoas que também gostam desse tipo de brincadeira.

    Se você conhecer algum desafio que siga essas linhas, deixe um comentário com a dica que eu coloco ele aqui com os devidos créditos.

    No fim do ano o colaborador mais ativo (que enviar mais desafios) ganha um jogo criativo da Gemini da escolha do ganhador.

    O desafio de hoje é o seguinte:

    O objetivo é ligar os números iguais sem que nenhuma linha cruze com a outra.

    A resposta está no vídeo abaixo (áudio em inglês):

  • 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á?