• Homem-Aranha: Um Dia a Mais  

    2008-11-20 22:11

    Vazio
    Vai é criar teia de aranha ali...

    Eu estava doido para escrever sobre isso, mas na época que saiu a revista Homem Aranha 82 seria um baita de um spoiler para quem ainda não havia comprado, e decidi esperar mais, não antes de mandar um email para a Editora Panini expressando a minha opinião sobre o que aconteceu.

    O negócio é o seguinte ... a tal "saga" "Um Dia a Mais" do Homem Aranha, onde o mentecapto do Joe Quesada fez a maior cagada que eu já vi desde comecei a ler HQ. E olha que já vi muita coisa brava como "A Morte do Super-Homem", "A Saga do Clone", "A Queda do Morcego", todas esses nhé-nhé-nhés dramáticos que eles aprontam para tentar vender mais revistas. Só que chega uma hora que o saco acaba e coincidentemente os caras extrapolam.

    Nessa "saga", a múmia do Quesada resolve apagar quase 20 anos de história do Homem-Aranha. É. Apagou. E de um dos jeitos mais bestas que tem para fazer isso: meteu uma entidade poderosa no meio (Mefisto), que, através de um pacto onde o Peter Parker e a Mary Jane decidiram entregar o seu amor e tudo que poderia vir de bom nele no futuro para o coisa-ruim, passou uma borracha na história do Amigão da Vizinhança para que a Tia May pudesse viver depois de ter levado um tiro que era para o Peter, que ficou naquelas de "é culpa minha blá blá blá". Ele estava conseguindo ser mais chato e reclamão do que o Surfista Prateado era nas histórias de uns 20 anos atrás. Mas enfim, o Peter voltou a ser solteiro, morando com a tia, andando de bicicleta, e ainda "resetaram" o Harry Osborn, o Flash Thompson e sabe lá quem mais.

    Pelo que eu li folheando na banca a seção de cartas do número 83, parece que os editores esperavam uma resposta mais negativa. Teve gente que argumentou que foi uma boa para "descomplicar" a história do Aranha e alguns que disseram que gostaram pois eles vão começar a ler agora praticamente do zero. Eu discordo dos primeiros, pois é batata que daqui alguns anos vão complicar tudo de novo. Quem está lendo as revistas importadas está mencionando que voltaram meio num estilo "anos 80" mais divertido, que eu duvido que dure muito. Quem começou a ler agora realmente levou vantagem se não liga para a história do personagem.

    Mas e nós, que lemos faz sei lá quantas décadas? Eu me senti o maior idiota do mundo por ter suportado todas aquelas fases medonhas acreditando que ia melhorar (e melhorou por vezes, eu estava gostando da Guerra Civil!), mas é tanta porcaria e tiro saindo pela culatra que acho que com o tempo a gente vai ficando de saco muito cheio. Tipo quando a Editora Abril começou a publicar a linha Premium, que custava caro e vinha com um discurso grande sobre qualidade blá blá blá. Aí os tontos (eu) compraram para na esperança de "dar uma força", tentar ajudar o mercado nacional de HQ não afundar, e após alguns meses eles cancelaram tudo e lançaram as revistas no papel mais vagabundo que eu já vi com outro discurso totalmente diferente, sobre ser acessível etc e tal.

    E essa "Um Dia a Mais" realmente para mim foi a maior forcação de barra que já vi e talvez a gota d'água para transbordar tudo. A imagem acima é da divisória da minha estante, especialmente feita para abrigar minhas HQs, onde até hoje estavam as revistas do Homem-Aranha publicadas pela Panini. Elas foram para o setor de "arquivo", juntamente com os Aranhas Premium e os formatinhos da Abril. Eu parei de ler Homem-Aranha depois de várias décadas. Chega. E se começarem com muito mais graça desse tipo, eu paro com outros também. X-Men estava uma chatice e aproveitei e parei junto. Ontem li que ano que vem estão querendo tirar o Bruce Wayne do manto do Batman. Ainda nutro alguma esperança que venha a ser alguma coisa decente por ter Neil Gaiman e Grant Morrison envolvidos, mas se começar com lenga-lenga de novo, eu paro também.

    Não sei se estou mais velho e chato, sem paciência, ou se esse pessoal realmente não aprende e fica insistindo nos mesmos erros de sempre, e o pior, piorando ainda mais. Podem estar apostando numa nova geração que nem compra a revista direito e só faz download pela web e afugentando quem era leitor fiel. Se eu penso que o mercado de HQ pode se abalar por causa de todas essas barbeiragens? Que se dane. O mercado de HQ pode se sustentar enquanto produzir coisas boas e tratarem os seus clientes, ou seja, nós, leitores, de forma justa. Se pararem com isso, não tem sentido de existir mais. Ainda bem que existem algumas revistas como a Pixel Magazine.

  • Half Full  

    2008-11-20 19:45

    Roy Fielding: When did you stop beating your wife, Ian? The apparent catalyst for this exchange was a document put out by Mike Smith. Clearly there are areas of disagreement.  Not just between Roy and Ian, but between many of the participants in the working group.  Many are focusing on such details as to what should be normative, and how such specifications should be arranged.  My opinion is that this discussion is premature and can’t be settled without first establishing a common understanding of the context and goals of the effort. Meanwhile, it is important to note the Mike’s document does not attempt to be inconsistent with the ongoing HTML5 work, it merely attempts to capture a subset of this information, and present it from a different perspective. The bigger problem is that HTML5 will be finished in 2022.  Or in 2010. The first thing that needs to be recognized is that the WHATWG’s goals (are these documented?) won’t be met in 2010.  The second thing is that neither will the W3C’s. There are many ways to address this.  One that I would not be in favor of is adjusting the W3C’s date significantly. IMHO, what’s needed is a chair that will take consensus whenever he (or she) can find it, and graciously accept defeat when that’s not possible.  Again, not all of the goals are achievable by 2010.  Whether they ever could have been or not is irrelevant, at this point it clearly is not the case. And it is worth repeating that the intent is that Mike’s document and Ian’s document aren’t in conflict.  One way to resolve any apparent conflicts is to relabel the document which is intended to be later.  Picking some dates out of the air, how about HTML5=2010; HTML6=2013; HTML7=2016; HTML8=2019; and HTML9=2022?  Meanwhile... Question: Do we need to resolve whether ping is in HTML9? Answer: Not today. Question: Do we need to resolve whether the DOM bindings are in HTML9 or are in a separate document? Answer: Not today. I could go on, but the basic point is that if HTML5 were redefined to be the set of things over which we can come to rough consensus over the next 18 months or so, it would in all likelihood be (a) delivered on time, (b) be significantly smaller than the current working draft, and (c) as consistent as we collectively know how to make it with the full “2022” vision. Repeat that process ever three or so years, and I’m confident that we will eventually converge on full consensus.  Possibly even by 2022.
  • Yogurt Oreo  

    2008-11-18 17:40

    Yogurt Oreo Do we now see the dawn of SSEO: Speach Search Engine Optimization?
  • Flay, eliminando repetições em código Ruby  

    2008-11-13 21:12

    Ronaldo - Programação

    Flay é uma ferramenta útil para cercear repetições de código em programas Ruby. Analisando o código semanticamente, o Flay é capaz de pegar repetições em casos que geralmente passam despercebidos no melhor dos códigos.

    Em algumas casos, a repetição será inevitável e mesmo necessária para deixar o código mais legível. Geralmente, entretanto, repetição significa código que ainda não está DRY o suficiente.

    Instalação

    Para instalar o Flay, basta rodar o comando gem:

    ~$ sudo gem install flay
    

    Esse comando instala a gem em si e um executável que pode ser usado na linha de comando.

    Um exemplo

    Para demonstrar o uso da ferramenta, vamos rodar o Flay contra o código mais recente do Active Record:

    O resultado (um trecho apenas), depois de alguns minutos de trabalho com a CPU a 100%, é o seguinte:

    Matches found in :defn (mass = 290)
      ./test/cases/associations/join_model_test.rb:237
      ./test/cases/associations/join_model_test.rb:262
      ./test/cases/associations/join_model_test.rb:271
      ./test/cases/associations/join_model_test.rb:623
      ./test/cases/associations/join_model_test.rb:650
    
    Matches found in :defn (mass = 270)
      ./test/cases/validations_test.rb:929
      ./test/cases/validations_test.rb:937
      ./test/cases/validations_test.rb:945
      ./test/cases/validations_test.rb:953
      ./test/cases/validations_test.rb:961
      ./test/cases/validations_test.rb:969
    
    Matches found in :defn (mass = 236)
      ./test/cases/validations_i18n_test.rb:491
      ./test/cases/validations_i18n_test.rb:510
      ./test/cases/validations_i18n_test.rb:529
      ./test/cases/validations_i18n_test.rb:549
    
    Matches found in :scope (mass = 204)
      ./test/migrations/duplicate/3_innocent_jointable.rb:12
      ./test/migrations/interleaved/pass_1/3_innocent_jointable.rb:12
      ./test/migrations/interleaved/pass_2/3_innocent_jointable.rb:12
      ./test/migrations/interleaved/pass_3/3_innocent_jointable.rb:12
      ./test/migrations/missing/4_innocent_jointable.rb:12
      ./test/migrations/valid/3_innocent_jointable.rb:12
    
    Matches found in :call (mass = 172)
      ./test/models/author.rb:64
      ./test/models/project.rb:17
    
    Matches found in :defn (mass = 171)
      ./test/cases/validations_test.rb:977
      ./test/cases/validations_test.rb:991
      ./test/cases/validations_test.rb:1079

    Note especialmente código que está sendo repetido em arquivos diferentes. No caso acima, como o código está em arquivos de teste, é provável que a repetição seja desejável para não poluir o escopo dos mesmos.

    Vamos tomar um outra caso na biblioteca em si:

    Matches found in :if (mass = 165)
      ./lib/active_record/associations/has_and_belongs_to_many_association.rb:82
      ./lib/active_record/associations/has_many_association.rb:100
      ./lib/active_record/associations/has_many_through_association.rb:194

    O código repetido em questão é o seguinte:

    if @reflection.options[:counter_sql]
      @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
    elsif @reflection.options[:finder_sql]
      # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
      @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
      @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
    else
      @counter_sql = @finder_sql
    end

    Vendo os arquivos, é possível observar que o código é o mesmo. Considerando que os três arquivos representam classes com um pai em comum seria possível reduzir a repetição movendo o código para um método a classe pai.

    Esse tipo de repetição tende a sumir com refatoramentos e o código acima provavelmente sumirá por um processo similar (alguém se habilita para um patch?).

    Conclusão

    Como é possível perceber, o Flay representa mais uma forma de observar o seu código em busca de coisas que podem ser melhoradas. É claro que isso não deve ser converter em uma obsessão de remover toda e qualquer repetição, o que demoraria mais tempo do que codificar coisas novas.

    Como de usual, sugestões, críticas e dúvidas são bem-vindos.

  • Flog, analisando complexidade em Ruby  

    2008-11-13 18:12

    Ronaldo - Programação

    Flog é outro analisador de complexidade para Ruby. Diferentemente do Saikuro, que mede a complexidade ciclomática real do código, o Flog tem o propósito de mostrar padrões de tortuosidade no seu código. É mais uma ferramenta interessante e de fácil uso para analisar a evolução da complexidade do seu código:

    Instalação

    Para instalar o Flog, basta rodar o comando gem:

    ~$ sudo gem install flog
    

    Esse comando instala a gem em si e um executável que pode ser usado na linha de comando.

    Um programa de exemplo

    Para demonstrar o uso da ferramenta, vamos utilizar o mesmo exemplo que mostrarmos no artigo sobre o uso do Saikuro.

    require "test/unit"
    
    class DocumentHasher
    
      def initialize(object, options = {})
        @object = object
        @options = options
      end
    
      def hash
        hash = { :id => @object.id }
        if @options[:fields]
          @options[:fields].each do |field|
            hash[field] = @object.send(field)
          end
        end
        if @options[:extra_fields]
          @options[:extra_fields].each do |field|
            hash[field] = @object.send(field.to_s + "_field")
          end
        end
        hash
      end
    
      def self.hash(object, options = {})
        self.new(object, options).hash
      end
    
    end
    
    class DocumentHasherTest < Test::Unit::TestCase
    
      Struct.new("HashExample", :id, :title, :author)
    
      TITLE_FIELD = "Test"
      AUTHOR_FIELD = "Author"
      EXTRA_FIELD = "Extra"
    
      def setup
        @object = Struct::HashExample.new(1, TITLE_FIELD, AUTHOR_FIELD)
        class << @object
          def extra_field
            "Extra"
          end
        end
      end
    
      def test_hash_id
        result = DocumentHasher.hash(@object)
        assert_equal 1, result[:id]
      end
    
      def test_hash_fields
        result = DocumentHasher.hash(@object, 
          :fields => [:title, :author])
        assert_equal TITLE_FIELD, result[:title]
        assert_equal AUTHOR_FIELD, result[:author]
      end
    
      def test_hash_extra_fields
        result = DocumentHasher.hash(@object, 
          :fields => [:title, :author], 
          :extra_fields => [:extra])
        assert_equal EXTRA_FIELD, result[:extra]
      end
    
    end

    Salve esse arquivo para um arquivo chamado hasher.rb e rode-o com o comando abaixo para verificar sua execução:

    ~$ ruby hasher.rb
    

    Utilização

    Com esse programa, podemos rodar o Flog. O comando é simples:

    ~$ flog hasher.rb
    

    Resultado inicial

    O resultado da execução acima pode ser visto na listagem abaixo:

    Total Flog = 57.7 (7.2 +/- 62.8 flog / method)
    
    DocumentHasher#hash: (27.3)
         9.0: send
         8.6: assignment
         5.8: []
         5.4: branch
         2.8: each
         1.9: to_s
         1.7: +
         1.5: new
         1.3: hash
         1.3: id
    DocumentHasherTest#setup: (8.3)
         6.5: sclass
         1.3: assignment
         1.3: new
         0.4: lit_fixnum

    Note que o método hash possui um fator de “tortuosidade” de 27 e que os maiores culpados são o envio de mensagens (send) e atribuições. A “tortuosidade” total do programa está em 57.7, que representa o cômputo de todos os métodos do programa.

    Alterando o programa

    Para fins de exemplo, podemos tentar reduzir a complexidade do método hash como fizemos no artigo anterior usando o exemplo abaixo:

    require "test/unit"
    
    class DocumentHasher
      
      def initialize(object, options = {})
        @object = object
        @options = options
      end
      
      def hash  
        hash = { :id => @object.id }
        build_fields(hash, @options[:fields] || {})
        build_extra_fields(hash, @options[:extra_fields] || {})
        hash
      end
      
      def self.hash(object, options = {})
        self.new(object, options).hash
      end
      
      protected
      
      def build_fields(hash, fields)
        fields.each do |field|
          hash[field] = @object.send(field)
        end
      end
      
      def build_extra_fields(hash, fields)
        fields.each do |field|
          hash[field] = @object.send(field.to_s + "_field")
        end
      end    
      
    end
    
    class DocumentHasherTest < Test::Unit::TestCase
      
      Struct.new("HashExample", :id, :title, :author)
      
      TITLE_FIELD = "Test"
      AUTHOR_FIELD = "Author"
      EXTRA_FIELD = "Extra"
      
      def setup
        @object = Struct::HashExample.new(1, TITLE_FIELD, AUTHOR_FIELD)
        class << @object
          def extra_field
            "Extra"
          end
        end
      end
      
      def test_hash_id
        result = DocumentHasher.hash(@object)
        assert_equal 1, result[:id]
      end
    
      def test_hash_fields
        result = DocumentHasher.hash(@object, 
          :fields => [:title, :author])
        assert_equal TITLE_FIELD, result[:title]
        assert_equal AUTHOR_FIELD, result[:author]    
      end
    
      def test_hash_extra_fields
        result = DocumentHasher.hash(@object, 
          :fields => [:title, :author], 
          :extra_fields => [:extra])
        assert_equal EXTRA_FIELD, result[:extra]
      end
      
    end

    Rodando o comando novamente, teríamos:

    Total Flog = 58.1 (5.3 +/- 10.3 flog / method)
    
    DocumentHasher#hash: (10.7)
         3.2: []
         3.0: branch
         2.6: assignment
         1.5: new
         1.3: build_extra_fields
         1.3: hash
         1.3: build_fields
         1.3: id
    DocumentHasher#build_extra_fields: (9.4)
         4.2: send
         2.8: assignment
         1.8: to_s
         1.6: +
         1.3: branch
         1.3: each
    DocumentHasherTest#setup: (8.3)
         6.5: sclass
         1.3: assignment
         1.3: new
         0.4: lit_fixnum
    DocumentHasherTest#test_hash_fields: (7.0)
         3.0: []
         2.6: assert_equal
         1.3: hash
         1.3: assignment

    Note que a complexidade dos métodos caiu substancialmente. Os métodos estão mais simples e mais balanceados. A “tortuosidade” geral subiu um pouco com a adição de algumas chamadas, mas, analisando os métodos individualmente é possível observar que a somatória de complexidade dos três métodos envolvidos é menor do que a complexidade original. Mais uma vez, o código está mais legível e mais testável.

    Conclusão

    O Flog representa uma métrica arbitrária de complexidade que, mesmo assim, pode ser usada para acompanhar o desenvolvimento de um base de código. Um exemplo disso pode ser visto no texto do Carlos Villela mostrando a evolução do código do Rails ao longo dos anos.

    Como de usual, sugestões, críticas e dúvidas são bem-vindos. No próximo artigo, esfolando o seu código Ruby com mais ferramentas de análise de complexidade.

  • Utensils  

    2008-11-13 14:05

    Patrick Mueller: We fundamentally have the wrong tools to do the job. We’re using a spoon where we should be using a backhoe. Look down! You’re using a spoon for bleep's sake! Don’t you realize it? Slap yourself around a little and clear the fog from your eyes. Expect better. I’ve seen applications that have been built using a backhoe.
  • Saikuro, complexidade ciclomática para Ruby  

    2008-11-13 02:49

    Ronaldo - Programação

    Saikuro é uma analisador de complexidade ciclomática para Ruby. A instalação e uso do mesmo são bem simples e serão descritos aqui brevemente.

    Instalação

    Para instalar o Saikuro, basta rodar o comando gem:

    ~$ sudo gem install Saikuro
    

    Esse comando instala a gem em si e um executável que pode ser usado na linha de comando.

    Um programa de exemplo

    Para demonstrar o uso da ferramenta, vamos utilizar um programa de referência. Esse programa será um simples transformador de objetos Ruby em hashes equivalentes e está listado abaixo, com os testes necessários:

    require "test/unit"
    
    class DocumentHasher
    
      def initialize(object, options = {})
        @object = object
        @options = options
      end
    
      def hash
        hash = { :id => @object.id }
        if @options[:fields]
          @options[:fields].each do |field|
            hash[field] = @object.send(field)
          end
        end
        if @options[:extra_fields]
          @options[:extra_fields].each do |field|
            hash[field] = @object.send(field.to_s + "_field")
          end
        end
        hash
      end
    
      def self.hash(object, options = {})
        self.new(object, options).hash
      end
    
    end
    
    class DocumentHasherTest < Test::Unit::TestCase
    
      Struct.new("HashExample", :id, :title, :author)
    
      TITLE_FIELD = "Test"
      AUTHOR_FIELD = "Author"
      EXTRA_FIELD = "Extra"
    
      def setup
        @object = Struct::HashExample.new(1, TITLE_FIELD, AUTHOR_FIELD)
        class << @object
          def extra_field
            "Extra"
          end
        end
      end
    
      def test_hash_id
        result = DocumentHasher.hash(@object)
        assert_equal 1, result[:id]
      end
    
      def test_hash_fields
        result = DocumentHasher.hash(@object, 
          :fields => [:title, :author])
        assert_equal TITLE_FIELD, result[:title]
        assert_equal AUTHOR_FIELD, result[:author]
      end
    
      def test_hash_extra_fields
        result = DocumentHasher.hash(@object, 
          :fields => [:title, :author], 
          :extra_fields => [:extra])
        assert_equal EXTRA_FIELD, result[:extra]
      end
    
    end

    Salve esse arquivo para um arquivo chamado hasher.rb e rode-o com o comando abaixo para verificar sua execução:

    ~$ ruby hasher.rb
    

    Utilização

    Com esse programa, podemos rodar o Saikuro. O comando é simples:

    ~$ saikuro -c -t -y 0 -w 11 -e 16 -i hasher.rb -o report
    

    Esse comando roda o analisador de complexidade com os seguintes parâmetros:

    • -t, para analisar tokens também
    • -y 0, para exibir a complexidade de todos os métodos
    • -w 11, para exibir warnings para métodos de complexidade superior a 11
    • -e 16, para exibir erros para métodos de complexidade superior a 16
    • -i hasher.rb, o arquivo a ser analisado (pode ser um diretório também)
    • -o report, o diretório onde gerar o relatório de saída

    Resultado inicial

    O resultado da execução acima pode ser visto, parcialmente, na imagem abaixo, que mostra os resultados somente para a classe em que estamos interessados:

    Saikuro 1

    Note que o método hash possui complexidade 5 que reflete os 4 comandos decisórios (2 if e 2 each) mais um ponto de saída pelo cálculo mais simples da complexidade. Os demais métodos, puramente lineares, possuem complexidade 1.

    No diretório de resultado há também um arquivo que mostra a quantidade de tokens gerados no programa. Este relatório, com os níveis de aviso baixo, tende a reportar erros mesmo em condições consideradas normais.

    Alterando o programa

    Para fins de exemplo, podemos tentar reduzir a complexidade do método hash. Digamos que façamos a seguinte alteração:

    require "test/unit"
    
    class DocumentHasher
      
      def initialize(object, options = {})
        @object = object
        @options = options
      end
      
      def hash  
        hash = { :id => @object.id }
        build_fields(hash, @options[:fields] || {})
        build_extra_fields(hash, @options[:extra_fields] || {})
        hash
      end
      
      def self.hash(object, options = {})
        self.new(object, options).hash
      end
      
      protected
      
      def build_fields(hash, fields)
        fields.each do |field|
          hash[field] = @object.send(field)
        end
      end
      
      def build_extra_fields(hash, fields)
        fields.each do |field|
          hash[field] = @object.send(field.to_s + "_field")
        end
      end    
      
    end
    
    class DocumentHasherTest < Test::Unit::TestCase
      
      Struct.new("HashExample", :id, :title, :author)
      
      TITLE_FIELD = "Test"
      AUTHOR_FIELD = "Author"
      EXTRA_FIELD = "Extra"
      
      def setup
        @object = Struct::HashExample.new(1, TITLE_FIELD, AUTHOR_FIELD)
        class << @object
          def extra_field
            "Extra"
          end
        end
      end
      
      def test_hash_id
        result = DocumentHasher.hash(@object)
        assert_equal 1, result[:id]
      end
    
      def test_hash_fields
        result = DocumentHasher.hash(@object, 
          :fields => [:title, :author])
        assert_equal TITLE_FIELD, result[:title]
        assert_equal AUTHOR_FIELD, result[:author]    
      end
    
      def test_hash_extra_fields
        result = DocumentHasher.hash(@object, 
          :fields => [:title, :author], 
          :extra_fields => [:extra])
        assert_equal EXTRA_FIELD, result[:extra]
      end
      
    end

    Rodando o comando novamente, teríamos:

    Saikuro 2

    Como os testes demonstram, o programa funciona da mesma forma. E como o relatório de complexidade também demonstra, a complexidade do programa permanece a mesma no geral e a dos métodos diminuiu. O resultado é um programa mais legível e mais facilmente testável.

    Conclusão

    Embora métricas em si não tenham nenhum poder de tornar o código de um programa melhor, acompanhar a evolução das mesmas é uma ferramenta fundamental para garantir a qualidade do código. Para quem usa o Ruby, o Saikuro é uma ferramenta simples e rápida para isso. Para quem não usa, é fácil achar ferramentas que se adequam a outras linguagens.

    Como de usual, sugestões, críticas e dúvidas são bem-vindos. No próximo artigo, ainda hoje, esfolando o seu código Ruby com mais ferramentas de análise de complexidade.

  • Conceitos de Programação: Complexidade Ciclomática  

    2008-11-13 02:23

    Ronaldo - Conceitos

    Este é o quarto artigo em uma série sobre conceitos de programação. Os demais artigos podem ser encontrados na página de resumo sobre a série. Como nos demais artigos, um aviso: o tratamento dado aos assuntos aqui é informativo e podem existir erros nos mesmos. Como nos demais artigos neste blog, comentários e correções são sempre bem-vindos.

    Complexidade Ciclomática

    Neste quarto artigo, vamos tratar um pouco de complexidade ciclomática. Apesar do nome esdrúxulo, complexidade ciclomática (também conhecida como complexidade condicional) é uma métrica simples para determinar, como o próprio nome sugere, a complexidade de um programa estruturado (cíclico).

    Essa métrica foi desenvolvida em 1976 por Thomas J. McCabe e reflete diretamente o número de caminhos independentes que um programa pode tomar durante a sua execução.

    Qualquer desenvolvedor que já tenha testado código em sua vida, sabe que a quantidade de testes necessária para exercitar um determinado trecho de código é diretamente proporcional à sua árvore decisória. Em outras palavras, quanto mais caminhos o código puder tomar (seja por meios de condicionais ou loops), maior a quantidade de testes necessários. E como veremos abaixo, há realmente uma relação direta entre a complexidade ciclomática e a cobertura de um código.

    Calculando a complexidade ciclomática

    Antes de mostrar exatamente como o cálculo pode ser feito, vamos observar algumas coisas em relação a um programa qualquer. Digamos que você esteja desenvolvendo um programa que lhe dê o maior divisor comum entre dois números. Uma fórmula simples é o algoritmo de Euclides que pode ser descrito da seguinte forma:

    Dados dois números naturais a e b, verifique se b é zero. Se sim, a é o maior divisor comum entre os mesmos; caso contrário, repita o processo usando b e o resto da divisão de a por b.

    Esse algoritmo pode ser expresso pelo seguinte programa em Ruby (note que ele não está em Ruby idiomático):

    require "test/unit"
    
    def euclid(m, n)
      if n > m
        r = m
        m = n
        n = r
      end
      r = m % n
      while r != 0
        m = n
        n = r
        r = m % n
      end
      n
    end
    
    class EuclidTest < Test::Unit::TestCase
      
      SETS = [[5, 10,  5], [2,  6,  2], [11,  7,  1], 
        [80, 64, 16], [2, 2, 2]]
      
      def test_euclid
        SETS.each do |set|
          assert_equal set[2], euclid(set[0], set[1])
        end
      end
      
    end

    Se o programa acima for executado, ele rodará o caso de teste logo abaixo da função que verificará se a mesma está correta. Você pode adicionar mais casos ao conjunto SETS se desejar.

    A função euclid pode ser descrita por um grafo simples que conecta os caminhos entre as várias declarações que a mesma contém. Esse grafo é o mostrado abaixo (clique para expandir):

    Com base nesse grafo, podemos definir a complexidade ciclomática de um programa da seguinte forma:

    CC = A - N + 2C
    

    Nessa fórmula:

    • CC é a complexidade ciclomática
    • A é o número de arestas do grafo
    • N é o número de nós do grafo
    • C é o número de componentes conectados

    Como se trata de uma função simples com um único ponto de entrada e saída, o número de componentes é 1 e a fórmula pode ser reduzida para:

    CC = A - N + 2
    

    Se a função possuísse múltiplos pontos de saída, entretanto, a complexidade ciclomática seria definida como:

    CC = A - N + C + R
    

    Nessa fórmula, R é o número de declarações de saída (em Ruby, o número de returns).

    Voltando ao grafo mostra na figura, vemos que o mesmo possui 11 nós e 12 arestas, o que nós dá uma complexidade ciclomática de 12 - 11 + 2, ou seja, 3.

    Uma outra maneira bem simples de descobrir a complexidade ciclomática é contar o número de loops fechados no grafo (que são formados por condicionais e loops) e somar ao número de pontos de saída. No grafo acima, temos 2 loops fechados (os if e while) e um ponto de saída, resultando no mesmo valor 3 para a complexidade da função.

    Uma coisa interessante é que a complexidade permanece a mesma quando a sintaxe de uma linguagem é levada em questão sem alterar a semântica do programa. Tome por exemplo a versão idiomática do algoritmo em Ruby:

    def euclid(m, n)
      m, n = n, m if n > m
      m, n = n, m % n while m % n != 0
      n
    end

    O grafo gerado nesse caso é (clique para expandir):

    Node que embora o número de nós e arestas tenha mudado, a relação entre eles não mudou e a complexidade permanece a mesma.

    Testando

    De uma forma geral, o valor da complexidade ciclomática define um limite superior para a quantidade de testes necessários para cobrir todos os caminhos decisórios no código em questão. Esse é um limite superior já que nem todos os caminhos são necessariamente realizáveis.

    Disso se infere que quanto menor a complexidade, menor a quantidade de testes necessários para o método em questão. Esse fato implica em outro curioso: quebra um método em vários reduz a complexidade dos métodos mas aumenta a complexidade geral do código e, de forma geral, mantém a testabilidade do programa completo no mesmo nível.

    Referências

    Obviamente, já que a complexidade é um valor específico, é possível extrair da mesma uma referência. Baseado no trabalho de McCabe, esses valores de referência são:

    • 1-10, métodos simples, sem muito risco
    • 11-20, métodos medianamente complexos, com risco moderado
    • 21-50, métodos complexos, com risco alto
    • 51 ou mais, métodos instáveis de altíssimo risco

    Conclusão

    Essa foi uma pequena introdução ao assunto com o objetivo de abrir o caminho para artigos posteriores mostrando ferramentas de apoio ao cálculo e monitoramento da complexidade ciclomática. Como de usual, sugestões e correções são bem vindos.

  • gae-sqlite  

    2008-11-12 16:20

    App Engine Fan: Last month, I wrote I would not post the sql connector's source code until it was a little more functional. Guess what? I changed my mind: you can find all the code at http://code.google.com/p/gae-sqlite/. From the code.google.com project page: This is a module that will allow you to run Google's App Engine development server with an SQLite backend. The concept is intended to be generic enough that other databases (mysql, SQL Server, Oracle...) can follow with only minimal modifications.
  • RDFaification  

    2008-11-12 14:29

    Shelley Powers: One unfortunate consequence of switching DOCTYPEs is that when I do use embedded SVG, the page won’t validate. However, this won’t impact on the user agents and their ability to process the SVG correctly, so I’ll just have to live with the invalidation errors. That’s the joy of DOCTYPEs. We either need a DOCTYPE that supports both RDFa and SVG and that a validator recognizes, or a validator that doesn’t rely on DOCTYPE that supports RDFa. oliver, Henri: suggestions?
  • Google App Engine  

    2008-11-11 17:49

    I've just started as a Developer Advocate for Google App Engine and there's been a lot of talk of "lock-in" recently. Google will have more to say officially in the future, as obviously my blog isn't the place to read about Google's official position, but I would like to point out the following things that have come up in conversation that people didn't know: The development kit is open sourced. All of the documentation for App Engine is CC licensed. Much of the underlying technology has been described in academic publications. Some of that underlying technology has been open sourced, such as protocol buffers. Also, you can see that other issues are on the roadmap to be addressed, such as bulk upload and download.
  • Native JSON parser  

    2008-11-11 12:11

    Mark Pilgrim: HTML 5 will not ... expose a native JSON parser for web content HTML 5 may not, but the plan is that ECMAScript will (section 15.12).  It is already supported in IE8 beta 2.  Firefox has landed support, and is tracking it as a part of an overall plan to support ES3.1.
  • Content Drives Demand  

    2008-11-07 21:40

    Mark Pilgrim: The
    element is a very straightforward HTML 5 feature that you can’t actually use yet. Jeff Schiller: These days, Internet Explorer is the last browser I look at… as long as all my textual content is actually visible, then that’s just fine by me. With apologies to Mark (and I truly understand where he is coming from), it seems to me that we would all be a lot better off if less people listened to him and more listened to Jeff. Frankly, I’m surprised that more WHATWG members don’t have the cojones to make use of straightforward markup innovations like the section element. Meanwhile, I think I’ll take a bit of Ian’s advice, and drop my use of the X-UA-Compatible header, making my blog’s front page simultaneously demonstrate both Mark and Jeff’s points.
  • Benefits of Uniformity  

    2008-11-01 13:30

    Jon Udell: These are, of course, best practices for an ecosystem sustained by web standards like URI, HTTP, and XML. But it was wonderful to see those best practices clearly demonstrated in a PDC keynote. In many ways it feels like we are now about at the point in the evolution of the web where we were two decades ago with respect to GUIs.  For that reason, it pleases me greatly to hear that Don Box was on stage demonstrating the value of Hi-Rest.  And it pleases me that Atom/AtomPub played a role in this. On the off chance that anybody is reading this, there is considerable value to be obtained from Lo-Rest.  But it comes at a great cost.  And the way forward is not to have servicebus.windows.net and user-ctp.windows.net/V0.1/Mesh/MeshObjects using different interfaces.  Instead it means adopting HTML as a carrier wave for your signal — which, yes, implies that you need to employ the modern equivalent of screen scraping.  And have to deal with cookies.  HTML is far from uniform: there are a lot of different tag soup parsers out there at various levels of quality.  Having everybody parse HTML consistently would be a huge step forward.  At the present time, HTML5 is the most comprehensive and complete approach to this.  Having Microsoft more actively participate, and spinning off and fast tracking just those parts of HTML5 necessary to implement this part of the HTML5 vision; well, that would be a huge win. And there are Hi-Rest analogs to this.  I’m confident that the service documents and feeds produced for the PDC demo were all well-formed, and valid.  As more people try to emulate this with PHP and templates, this won’t be true quite as much.  The first time a “smart quote” or a non-Latin1 character gets into the stream, it will fall over.  Having everybody recover in the same manner would also be a big win, though not quite as huge as it is for HTML.  Anne calls his explorations into this topic XML5.
  • Ubuntu on a Stick  

    2008-10-31 18:16

    My plans are to do a fresh install of Intrepid Ibex on a number of computers, and since I had a spare machine, I thought I would recover Windows to a known clean slate, insert a second hard drive, and use that install to clone existing machines which would then be wiped and restored. A few bumps along the way, none of which affect my overall plan. The recovering of Windows went fine, as did the installs of the various updates. My first problem occurred when I tried to boot Ubuntu from CD-ROM.  The machine wouldn’t recognize the disk.  Other machines booted fine from this disk.  So I tried another (unrelated) disk, and it wouldn’t recognize it either.  The BIOS and Windows recognized that there was a CD reader, and could even be told to eject a disk, but couldn’t read anything. I swapped out the CD reader for another one.  Same symptoms.  I swapped out the IDE cable.  Same symptoms. Stumped, I decided to try out the new bootable USB maker.  System » Administration » Create a USB startup disk and within a few minutes I was happily installing. After I installed Ubuntu on the second drive and rebooted, I was into Windows.  So I went into BIOS setup and reversed the boot order of the hard drives, and could boot into Ubuntu My next problem was after I booted.  I got to the login screen, but after a successful login, all I could see was a background.  I could not Ctrl-Alt-Delete.  I could not Ctrl-Alt-F2 to see an alternate console.  But by rebooting and then doing a Ctrl-Alt-F2 before logging in, I could log in.  Looking at my Xorg.0.log file, I saw a number of messages like this: [mi] EQ overflowing. The server is probably stuck in an infinite loop. [mi] mieqEnequeue: out-of-order valuator event; dropping. Based on this bug, I removed compiz and compiz-core and could then login to Gnome. Finally, I could no longer boot via grub into Windows, but these instructions worked like a champ.
  • OpenId Minus Id Equals Wide Open  

    2008-10-29 12:07

    Martin Atkins: Yahoo!'s OP and now it seems Microsoft’s OP both ignore the value of openid.identity provided to them, and just return an assertion for whatever user’s logged in. I may ultimately need to black-list such ids. Looking at live.com instructions: At any Web site that supports OpenID 2.0, type openid.live-INT.com in the OpenID login box to sign in to that site by means of your Windows Live ID OpenID alias. If everybody uses the same URI, I can’t tell them apart.  That doesn’t concern me much, but do find it a bit distressing that that’s the recommended usage. What concerns me is that people may use such a URI for delegation.  If Jorgen, for example, were to add such a generic URI as his openid.delegate link, then anybody who has a windows live id could authenticate using his blog URI. What concerns me more is if somebody follows these instructions for delegation.  Then anybody with a Windows Live id could authenticate using his blog. I note that Jorgen left a comment on Martin’s blog using http://openid.live-int.com/jt.  As long as that URI is uniquely his, and can’t be used by anybody else, that’s fine.
  • Algumas dicas para testes melhores  

    2008-10-27 14:08

    Ronaldo - Programação

    O Lucas Húngaro escreveu recentemente sobre a necessidade de abandonar testes quebradiços com uma forma de manter seus testes saudáveis, citando vários exemplos de como isso pode ser feito. Desnecessário dizer, concordo com o que ele disse.

    Um conselho dele em particular me parece a melhor consideração sobre o assunto. Falando sobre mocks e stubs, ele diz:

    Algumas soluções pra isso são: não utilizar mocks e stubs (o que é um tanto extremista), aumentar o encapsulamento das classes ou aceitar isso e continuar.

    Chamo a atenção para a parte final: algumas vezes, você tem simplesmente que aceitar que alguns testes devem ser de certa forma para garantir uma cobertura mais compreensiva.

    Dito isso, seguem algumas dicas para testes melhores:

    Mocks e Stubs

    Uma regra simples que pode ajudar a entender quando usar essas duas técnicas é a seguinte: Se o comportamento esperado deve ser conhecido, use mocks e stubs. Caso contrário, não.

    Isso torna fácil perceber que testes de modelos–no caso do Rails, por exemplo–raramente (ou nunca) deve usar essas técnicas. Quando se trata do domínio da aplicação, encapsulamento é uma propriedade básica de sistemas orientados a objetos que não deve ser violada se possível. Obviamente, abstrações vazam; mas isso não deve ser uma desculpa para quebrá-las em testes.

    Não teste a abstração imediatamente inferior

    Se você está usando Rails, por exemplo, não faz o menor sentido testar a abstração inferior. É muito comum ver testes como:

    it "should return all current books" do
      Book.current.proxy_options.should == { :conditions => { :current => true } }
    end
    

    Esse tipo de teste verifica se o Rails está funcionando da maneira como deve funcionar e não se o seu código está correto. O resultado é que pequenas mudanças e principalmente a introdução de algum comportamento ortogonal pode quebrar completamente a aplicação enquanto o teste continua a funcionar.

    Digamos, por exemplo, que você agora tenha que usar alguma esquema de caching em sua aplicação. No interesse de que a abstração não vaze, você envolve o método acima em alguma abstração que faça isso. O teste acima provavelmente quebrará de imediato, porque está testando a abstração inferior e não o comportamento da aplicação.

    O teste correto, nesse caso, seria verificar se a lista retorna é realmente correta, mesmo que isso envolva alguma manipulação de dados que reduza a velocidade do teste.

    Use testes de sanidade

    Conversamente, é importante também ter alguns testes de sanidade. Às vezes é necessário saber se alguma coisa está declarada, mesmo que a forma final daquela declaração não seja importante.

    Um exemplo disso são testes em views. Geralmente, testar se algum conteúdo está aparecendo em uma view é inteiramente desnecessário e pode gerar testes que devem ser refeitos a cada pequena mudança para se adaptar a novas condições.

    Mesmo assim, em alguns casos é importante a existência de testes que correm esse risco como forma de testar a sanidade da aplicação; em outras palavras, testar se condições básicas estão sendo supridas.

    Não teste nada que não seja público

    Uma pergunta muito comum em listas de discussão é como testar métodos privados ou protegidos de uma classe. A reposta geralmente envolve o uso de reflexão e violação de abstrações.

    Um exemplo comum seria o código abaixo:

    it "should prepare the environment" do
      Manager.send(:prepare_environment)
      Manager.environment.name.should == "development"
    end
    

    A resposta correta deveria ser: nunca faça isso. Se seus testes contém alguma necessidade desse tipo, você está violando o encapsulamento e isso indica alguma problema arquitetural mais básico da aplicação. Provavelmente alguma necessidade de separação de dependências que você ainda não identificou.

    Nesse casos, funcionalidade básica que envolva código encapsulado pode ser melhor testada pelo seu efeito em outras partes da aplicação.

    Cuidado com a ordem dos seus testes

    Uma coisa interessante é tentar rodar os seus testes em ordem arbitrária. Existe opções para isso em alguns dos frameworks existentes que podem ajudar a identificar dependências escondidas em seus testes.

    Finalizando

    Para terminar, esse são apenas alguns dos muitos conselhos possíveis nessa área. Fiquem à vontade para usar a área de comentários do texto para expandir sobre o assunto.

    Um conselho final: use seus testes como oportunidades para pensar sobre a sua aplicação. Se você se vê em uma situação em que testar está se convertendo em algo progressivamente mais complicado, alguma coisa está errada na forma como você desenhou seu código. Pare, pense sobre o assunto, e refatore. Seu código e seu tempo agradecem.

  • Algumas considerações sobre TDD e BDD  

    2008-10-27 10:32

    Ronaldo - Rails

    Parece haver muita confusão sobre a diferença entre TDD e BDD na comunidade Rails. A criação de vários frameworks competidores (RSpec, Shoulda, Expectations, etc) parece não ter ajudado muito já que cada proponente torce um pouco a visão dada por cada um deles.

    A grande divisão

    O problema é que a maioria das pessoas que pratica uma variante ou outra nunca leu a literatura sobre o assunto e por causa disso há uma falta de formalização sobre o assunto. Muito do que se vê escrito hoje tanto sobre TDD ou BDD é escrito da forma que aquela pessoa em particular pratica o assunto e não como o conceito foi sendo desenvolvido ao longo das anos.

    Tome, por exemplo, TDD como popuralizado originalmente por Kent Beck no contexto de Extreme Programming. Existe uma granularidade envolvida no crescimento do código que pode ser descrita como orgânica.

    Design ou Development?

    Uma das coisas que sempre me ajudou a enxergar o modo apropriado de construir uma aplicação a partir de testes foi pensar no D final de TDD ou BDD não como desenvolvimento, mas como design.

    Voltando ao tema de granularidade, se você fizer uma experimento e acompanhar dezenas de pessoas que “praticam” TDD ou BDD, o mais provável é que você perceba que os testes estão sendo escritos como uma forma de cobrir o código e não como uma forma de pensar no código em si. Um teste é escrito que demonstra um certo caso de uso da classe ou método em questão e o código para aquele trecho é então escrito.

    É muito comum, dentro da prática descrita acima, que vários testes seja escritos em seqüência e o código da classe ou método inteiro seja depois implementado. Voltando ao modo como Kent Beck explicou o processo originalmente, nada por estar mais longe da realidade.

    De fato, nos seus exemplos concretos sobre o assunto, Kent Beck compara o uso de TDD ao uso de uma lista de tarefas para a construção de código, o que força o desenvolvedor a pensar em detalhes sobre a arquitetura que está criando. Volta aqui o sentido de design e não de desenvolvimento puro.

    Chega-se então ao ponto de que cada teste é pensado como uma forma de evoluir a arquitetura do código e pensar em como aquela classe ou método existe dentro do contexto mais generalizado da aplicação. Não é surpreendente que quando isso é feito, coberturas de 100% são factíveis, métodos ou funções não possuem mais do que algumas poucas linhas fazendo exatamente e o que deve fazer e não mais e uma delegação efetiva de responsabilidades acontece.

    TDD versus BDD

    Chega-se então ao ponto central da conversa? Qual é a diferença principal entre TDD e BDD? No ponto mais básico da cadeia, a resposta é: nenhuma.

    Tanto TDD quando BDD são formas de se pensar em testes partindo de premissas ligeiramente diferentes mas chegando ao mesmo resultado prático. Sendo feitas de maneira apropriada, o resultado final dentro das necessidades acima (cobertura, delegação, arquitetura) é o mesmo e a conversação que deve acontecer no desenvolvimento ocorre naturalmente.

    Apesar disso, há uma diferença histórica que está evoluindo com o tempo e que, em última instância, está se convertendo no foco de debate entre os dois campos. Essa diferença foi inicialmente expressa por Dan North (responsável pelo desenvolvimento do RBehave, que mais tarde se tornaria a base do Story Runner do RSpec) e depois elaborada por David Chelimsky.

    Esse posicionamento coloca BDD não como uma oposição direta a TDD em termos práticas, mas como uma corrente filosófica que engloba TDD e expande o discurso para incluir elementos que vão além do metodológico e já começam a cair no processual.

    Um exemplo claro disso é a ascensão de frameworks orientados a estórias (testes de aceitação de usuários) como o Cucumber. Esses esforços representam a graduação de BDD não somente com uma forma de fazer testes mas como um processo de pensar e executar testes ao longo de toda a gama de necessidades de uma aplicação (de unitários e visuais).

    Essa conversa é a parte mais importante do BDD e, no caso do Rails e Ruby, o RSpec representa apenas uma faceta de tudo o que vem sendo pensado pela comunidade. Eventualmente, como Chelimsky coloca, TDD será um parte de algo maior que será chamado de BDD.

    Finalizando

    A confusão experimentada atualmente é mais resultado da conversação acima do que da diferença real entre os frameworks. Ainda citando Chelimsky, quando BDD era somente sobre testes unitários, não havia diferença nenhum entre TDD e BDD. Ambos eram e são sobre a melhor forma de testar uma aplicação e nada mais do que isso.

    Para dar um exemplo, atualmente eu tenho preferido a combinação Test/Unit + Shoulda + RR ao invés do meu usual RSpec. Gosto da leveza dos anteriores em relação ao último quando os testes se tornam mais envolvidos. Mas isso representa apenas dores de crescimento do RSpec. É bem possível que quando este último estiver mais estabilizado, lidando mais com a magia negra que ele precisa para sua sintaxe, eu mude novamente de prática.

    De uma forma geral, então, escolha o frameworks que melhor lhe convier. Desde que a forma como você faz testes esteja correta, o resultado final estará correto também.

  • Quem imaginaria?  

    2008-10-25 03:12

    Ronaldo - Mundo

    Lendo essa entrada no blog do Jon Taplin (muito recomendado, por sinal) é interessante ver que o Brasil figura entre as economias emergentes de menor vulnerabilidade durante a crise econômica atual. De fato, entre os países listados abaixo, ele é o menor vulnerável.

    Isso não quer dizer, é claro, que ele não seja muito vulnerável. Como Taplin mesmo aponta, o governo está tendo que queimar milhões de reais para manter a moeda em um nível de valorização razoável. Da mesma forma, a queda da bolsa brasileira é uma das mais acentuadas.

    Mesmo assim, é interessante ver que, na situação atual, o Brasil está em uma posição bem diferente da imaginada há alguns anos atrás. Ninguém apostaria suas fichas nem de longe em um Brasil com um economia estável e um poder de influência razoável no mercado global. Se nem tudo são flores, pelo menos não há um pânica generalizado em um momento que outros já estão em plena fuga.

    Agora, se ao menos houvesse um jeito de exilar todos especuladores…

  • Aprendendo Rails  

    2008-10-23 13:22

    Ronaldo - Rails

    O dia nove de outubro passado marcou os dois anos da liberação do meu tutorial Rails para sua Diversão e Lucro. Oito meses depois do lançamento, o mesmo já havia ultrapassado 15 mil downloads somente no meu servidor. Considerando que a licença é GFDL e que o mesmo foi reproduzido em dezenas de sites, o número atual deve ter sido bem maior mesmo na época.

    No geral, foi uma experiência bem positiva. Para um tutorial que começou como material para um curso, a quantidade de pessoas interessadas foi surpreendente. Mesmo na época, havia uma carência enorme de informações sobre o assunto. Coincidentemente, o primeiro livro de Rails no Brasil também foi lançado na mesma época. Acho muito legal, inclusive, que pessoas que hoje são extremamente ativas na comunidade tenham tido contato inicial com Rails através do tutorial.

    Bem, esse monte de reminiscências é para anunciar aos leitores regulares que possam eventualmente desejar aprender Rails e aos visitantes que volta e meia chegam aqui perguntando sobre o assunto e topando com um tutorial desatualizado que uma nova versão está disponível.

    No melhor espírito da GFDL, Cássio Souza atualizou o tutorial para Rails 2.1, modificando o material antigo, removendo as partes ultrapassadas e acrescentando muito material novo em um novo tutorial que reflete mais precisamente a versão atual do Rails (de 1.2 para 2.1). O material também é acompanhando de screencast que explica mais detalhes práticos sobre o assunto.

    Fica aí a dica para quem quer aprender sobre Rails de maneira prática. E que venha os downloads. :)

  • A falsa dicotomia: generalista versus especialista  

    2008-10-23 00:57

    Ronaldo - Trabalho

    Um papo semi-presente na área de TI é a velha dúvida: generalista ou especialista? Como em essencialmente qualquer carreira em que a dicotomia é possível, há sempre uma grande questão em torno do que decidir quando se pensa em um plano para o futuro.

    De um lado, especialistas justificam sua escolha em termos de melhores salários, pouco rotatividade, maior reconhecimento. Do outro lado, os generalistas justificam com maior estabilidade, menos rotina, maior amplitude de oportunidade. Sempre há uma certa tensão entre as duas versões do profissional de TI e como qualquer dicotomia, geralmente a verdade é uma coisa inteiramente diferente.

    No tempo que eu tenho de carreira, o que eu observo é que a dicotomia é inteiramente falsa. Se existe o bom profissional, não há como o mesmo ser especialista ou generalista. Em um área como a nossa, onde a velocidade de transformação é enorme, medida em meses e semanas ao invés de décadas e anos, qualquer coisa que não seja a fusão dos dois conceitos é simplesmente impensável.

    Ironicamente, algumas das raízes dessa separação refletem, ou talvez até tenham sua origem, na antiga disputa entre consultores e empreendedores–centrada, por sua vez, na igualmente inválida dicotomia entre engenheiros e artesãos. Como a maioria das comparações feitas para explicar o desenvolvimento de software, as analogias passam longe de explicar a realidade.

    Recentemente, a popularização de frameworks e linguagens fora do mainstream como Rails/Ruby e Django/Python permitiu uma rara observação de como qualquer separação explícita geralmente é de aplicação limitada.

    Um caso usual é tomar um profissional que trabalha com uma dessas tecnologias, que está tentando sair do comum em seu trabalho, seja como forma de criar uma nova carreira ou mudar sua empresa para obter uma carreira mais interessante, e por via dessa necessidade forja uma aliança entre áreas díspares como gerência, design, usabilidade, programação e outras (às vezes acumulando todas essas funções sobre si mesmo) e usar isso como exemplo de que o generalizado é melhor do que o especializado.

    O mesmo erro é tomar uma disciplina como medicina e usar para ilustrar a outra proposição. Enquanto o exemplo do parágrafo anterior é um erro ao assumir que o mesmo é um exemplo de arte, o segundo é um erro em assumir que “engenharia” é uma metáfora inteiramente válida para o desenvolvimento de software.

    A realidade é que, como em qualquer área, o nosso grau de proficiência é inteiramente dependente do âmbito do que estamos fazendo no momento e das circunstâncias transitórias em que esse trabalho se exprime. A especialização, nesse sentido, vem mais como força da acumulação de experiência do que de uma escolha explícita.

    Um desenvolvedor é um engenheiro? Sim. É um artista? Também. Por extensão, a mesma pergunta pode ser feita para a dicotomia inicial. É um generalista? Sim. É um especialista? Também.

    Isso é o que Dave Thomas fala quando explica sobre o Dreyfus model. Você vai de um lado do espectro ao outro e às vezes está em vários pontos em várias áreas. Quando isso é considerado, a dicotomia cai imediatamente por terra.

    Em última instância, como a maioria das preocupações similares, o único resultado da eterna discussão é o gasto de tempo e calorias que poderiam ser postos para melhor uso em projetos mais interessantes. Na próxima vez que alguém quiser discutir isso com você, dê de ombros e ignore. Você não estará perdendo nada de valor para sua carreira futura.

    Atualização: A propósito, acabaram de chamar a minha atenção que o Carlos Brando e o Fábio Akita fazem essencialmente o mesmo ponto no Rails Podcast Brasil #34. Desnecessário dizer, concordo.

  • Como blogar efetivamente, ficar famoso e ganhar leitores sem parecer...  

    2008-10-20 08:50

    Ronaldo - Blogging

    O Google registra nada menos que 77 milhões de referências a “como blogar”. Milhares de conselhos dispensados por famosos e não famosos que tentar ensinar a outros (e convencer a si próprios) como chegar à ponta da vasta cauda longa que compreende as múltiplas e dissonantes blogosferas espalhadas por aquilo que se convencionou chamar a Web.

    O que nenhum desse milhões de textos diz é que a verdade é bem mais simples do que parece. Neste pequeno texto, chame de cartilha se quiser, apresento o método mais efetivo, mais seguro e mais recompensador de se erguer aos pináculos da sua blogosfera de escolha. E isso tudo sem o inconveniente de parecer um plagiador.

    O método descrito abaixo compreende a criação de um formato específico de artigo, necessariamente longo–o que significa que a técnica só vai funcionar para aqueles com paciência de escrever algo além das médias três linhas que compõem o usual texto de um blog–dispensado aos leitores em doses ocasionais, duas a três vezes por semana. Esse texto possui seções específicas que agora descrevo:

    Parte I - Do assunto

    O primeiro passo é escolher um tema que esteja em voga. Atualmente, por exemplo, pode ser algo relacionado à crise econômica, Obama versus McCain, o sempre presente fluxo de start ups Web, os lançamentos de produtos de firmas Web famosas e assim por diante. Se você não consegue ter idéias, basta visitar um site de notícias Web qualquer e usar a inspiração.

    Como é possível ver, escolher o assunto é muito simples. Basta reusar algo que está sendo falado ou que está no “inconsciente coletivo” da Web no momento. Você vai perceber depois de alguns artigos que não é necessário nenhum tipo de originalidade. Afinal de contas, ninguém é original.

    Parte II - Do título

    O título, novamente, é tão simples quando escolher o assunto. Na verdade, existem somente três tipos de títulos que levam ao estrelato. A menos que você queira variar ocasionalmente, para surpreender um ou outro leitor, você deve ser conformar a estes três padrões, que são:

    • Como [alguma coisa]. Por exemplo, Como blogar efetivamente, ficar famoso e ganhar leitores sem parecer um plagiador; ou Como entender as eleições americanas, ou Como ser um marketeiro eficaz.
    • [N] passos para [alguma coisa]. Por exemplo, 5 passos para blogar efetivamente, ficar famoso e ganhar leitores sem parecer um plagiador; ou 3 frases para entender as eleições americanas, ou 9 atitudes para ser um marketeiro eficaz.
    • D[a/as/o/os] um [assunto qualquer]. Por exemplo, Dos passos para blogar efetivamente, ficar famoso e ganhar leitores sem parecer um plagiador; ou Das necessidades de entender as eleições americanas, ou Das vantagens de ser um marketeiro eficaz.

    Como é possível perceber, não existe esforço nenhum em criar um título eficiente. Basta adaptar alguma frase pequena nos moldes acima e você terá um pagerank vencedor à sua espera.

    Parte III - Da introdução

    Depois de falar na parte fácil, é hora de falar da parte mais difícil. Como o seu professor de literatura da quinta série costumava dizer, a introdução e a conclusão são duas das partes mais importantes de um texto. Já que só sobra o desenvolvimento, acho que esses professores estavam pensando em seus alunos como blogueiros futuros.

    Mas, infelizmente, tais professores estavam certos. A parte mais difícil de um texto vencedor é a introdução. E isso porque, de todo o resto, é a única que vai exigir um módico de originalidade. Se você começar a copiar na introdução, vai ser sacado rapidamente. Mas não se preocupe, como uma boa introdução não passa de dois ou três parágrafos–que podem ser curtos–o risco de errar é mínimo.

    Para uma boa introdução, basta criar esses dois os três parágrafos–que podem ser duas ou três linhas na verdades, mas não se esqueça das quebras para torná-los mais convincentes–com alguma citação famoso seguida de uma declaração sobre como você vai iluminar o assunto. Varie isso a cada artigo e os leitores vão achar que você é, além de tudo, um erudito.

    Parte IV - Do conteúdo, ou, da verborréia necessária

    Se a introdução é complicada, o conteúdo em si é absurdamente simples. Tudo o que é necessário é uma quantidade razoável de verborréia parafraseadas, citada e puramente copiada e você está pronto para a glória.

    Para acertar a mão, basta seguir os passos abaixo:

    1. Primeiro, encontre um autor que tenha escrito algo que seja pertinente ao seu assunto (mesmo que apenas de leve) e que esteja em voga. Atualmente, livros como A Lógica do Cisne Negro, Blink, The Tipping Point, Freakonomics, The Long Tail, The Big Switch, Linked ou qualquer outra nessa linha de assuntos modernosos que se propõem a explicar tudo sobre tudo.

    2. Livro escolhido, você precisa agora seguir um padrão bem simples: três parágrafos de paráfrase, ou seja, falando exatamente o que o autor falou em algum ponto do livro com suas palávras, e um parágrafo citando o autor do livro. Para cada conjunto desse, coloque um título parafraseado do próprio lido ou inventado na hora mesmo.

    3. Para não ficar ainda mais interessante, a cada dez ou doze parágrafos, introduza alguma citação ou paráfrase de outro autor. Se leitores vão achar você ainda mais erudito pela profundidade das correlações.

    E é só. Desde que o artigo seja grande, o esforço será notado. Caso você não queira se dar ao trabalho de ler um livro ou mesmo escaneá-lo em busca de citações interessantes, um artigo em uma publicação da sua área também serve. Seja a InfoQ, Wired ou The New York Times, o importante é contar a estória direito.

    Parte V - Da conclusão

    Finalizar, ao contrário do que o seu professor dizia, não é tão complicado. Basta criar alguma expectativa no leitor de como ter lido aquele artigo mudou a vida dele ou explicar como, de posse das informações que você passou, que você tão cuidadosamente colou em um artigo de proporções avassaladoras, vai transformar a sua compreensão de tudo o mais e torná-lo mais erudito, famoso ou eficiente. Qualquer dessas palavras ou similares em uma frase já é vencedora.

    Conclusão

    Como você pode ver, o processo todo é simples (embora um pouquinho trabalhoso). Mas, como já diziam os antigos, no pain, no gain. Basta seguir as instruções, gastar uns 20 ou 30 minutos coletando citações de suas obras favoritas e você terá algo original, digno de uma mestrado ou doutorado.

    Para terminar, depois de postar o texto, sente-se e aguarde as dezenas de comentários congratulando você por sua elegância, erudição e compreensão do mercado. Depois de tanto trabalho, você mereceu.


    P.S.: Disclaimer Eu presumo que os leitores regulares vão perceber imediatamente que o texto acima é irônico. Mas, como várias pára-quedistas podem pensar o contrário, fica aqui o disclaimer. Não vai adiantar muito, provavelmente, mas o seguro morreu de velho.

  • Se divertindo com encodings no Ruby 1.9  

    2008-10-17 15:17

    Disclaimer: Post descaradamente copiando um do Dave Thomas, mas que vai servir para resolver uma dúvida que apareceu durante o Rails Summit Latin America.

    Pronto, dito isso, posso continuar. :-) Apareceu uma dúvida da platéia sobre como fazer pluralizações em atributos do ActiveRecord que utilizam acentuação nos seus nomes. Apesar de achar estranho usar acentuação (cada um, cada um né), uma coisinha interessante que vai vir no Ruby 1.9 é o suporte de encodings diferentes nos arquivos (e em fluxos de IO, Strings, regexps, símbolos, etc, como bem disse o Dave). Isso permite fazer algumas coisinhas interessantes como ele mostrou no artigo original, do qual eu derivei esse exemplo:

    1 # encoding: utf-8
    2 module Enumerable
    3    def4       self.inject {|memo,val| memo += val}
    5    end
    6 end
    7 
    8 puts [1,2,3].∑
    9 puts (0..3).∑
    

    Interessante hein? Para quem quiser inserir esses caracteres malucos no Vim, consulte o help dos digraphs com :help digraphs. Esse do exemplo é feito usando, no modo de inserção, CTRL+K +Z.

  • Datas alteradas no horário de verão  

    2008-10-14 14:04

    Tive um problema aqui em um procedimento que faz algumas verificações de arquivos nas máquinas dos usuários. Para não dar nada, eram usuários daquele sistema operacional legal que insiste em nos dar algumas surpresinhas malucas de vez em quando. Eu achei estranho, fui dar uma fuçada para ver qual era a razão do problema e encontrei uma situação bem interessante. Vamos levar em conta o seguinte programa simples em C chamado conf.c:

     1 #include <time.h>
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <sys/stat.h>
     5 
     6 int main(int argc, char** argv){
     7    struct stat   stDir;
     8    if(argc<2)
     9       exit(1);
    10 
    11    printf("Checando %s\n",argv[1]);
    12    if(stat(argv[1],&stDir)<0)
    13       exit(1);
    14 
    15    printf("%ld %s\n",stDir.st_ctime,ctime(&stDir.st_ctime));
    16    return 0;
    17 }
    

    Esse programa faz uma coisa bem simples: verifica a data e hora da última alteração do status de um arquivo (ou um diretório) usando a função stat, que retorna algumas informações na estrutura stat, de onde eu formato a data de criação do arquivo usando a função ctime. Nada de complicado. Rodando esse programa no GNU/Linux duas vezes, alterando a data para uma data dentro do horário de verão entre as chamadas, tenho o seguinte resultado:

    [taq@taq~]$ date
    Ter Out 14 09:51:30 BRT 2008
    [taq@taq~]$ conf .mozilla/
    Checando .mozilla/
    1205268188 Tue Mar 11 17:43:08 2008
    
    [taq@taq~]$ date
    Sáb Out 25 12:00:03 BRST 2008
    [taq@taq~]$ conf .mozilla/
    Checando .mozilla/
    1205268188 Tue Mar 11 17:43:08 2008
    

    Ok, o meu diretório ~/.mozilla foi criado em 11/03/2008 17:43:08, que foi comprovado em ambas as chamadas. Agora vou fazer a mesma coisa no "outro sistema":

    C:\>date
    Data atual: ter 14/10/2008
    Digite a nova data: (dd-mm-aa)
    
    C:\>conf c:\windows\temp
    Checando c:\windows\temp
    1201084518 Wed Jan 23 08:35:18 2008
    
    C:\>date
    Data atual: sáb 25/10/2008
    Digite a nova data: (dd-mm-aa)
    
    C:\>conf c:\windows\temp
    Checando c:\windows\temp
    1201088118 Wed Jan 23 09:35:18 2008
    

    Uai! Alterando a data para uma dentro do horário de verão, a data do arquivo retornada muda também? Achei a causa do problema na verificação dos arquivos! O timestamp original do arquivo nunca vai bater com o retornado atualmente.

    Qual é a razão disso eu ainda não entendi direito. IMHO o retorno da data do arquivo não deveria ser alterada nunca e o procedimento do GNU/Linux é que está correto, pois independente da situação corrente o arquivo foi criado em uma determinada data e hora, que no segundo caso era 23/01/2008 08:35:18. Fica aí a dica se alguém tiver algum problema do tipo.

    Outra dica: se alguém precisar usar o gcc para o segundo teste, pode utilizar o MingW. O programa compila de boa utilizando gcc -o conf.exe -Wall -D_GNU_SOURCE conf.c.

  • Fissurados no Vim  

    2008-10-10 12:13

    Fissurados no Vim

    Nem preciso dizer que eu sou um desses malucos. Mas não tenho isso aqui em casa não! :-)

    P.S.: Podia ter colocado só :x, mas o :wq é mais ... clássico. :-)

    P.S.2: As requested, here's a link to the English version.

  • Um jeito high tech de pegar bebuns  

    2008-10-08 20:18

    Mail Googles

    Vai falar que esse não é um jeito cool de pegar os bebuns, usando o Mail Googles? Não precisa de bafômetros superfaturados e ainda pode entrar na onda do 3G e dos minis.

  • gem install ibm_db  

    1970-01-01 00:00

    Antonio Cangiano: The IBM API development team has released version 1.0 of the ibm_db gem, which includes both the Ruby driver, and the Rails adapter, for IBM’s databases. This was just over three years in the making.  Perhaps there is even some of my original code in there — though in reality, that matters not.  In the six months prior to that post, I spent a fair amount of time in meetings with people discussing whether it should be done, could it be done, and how much would it cost.  Addressing all three at once was too hard, so ultimately I decided to implement just enough code and test cases to demonstrate that it could be done, and no more.  This enabled those within IBM who organizationally should be responsible for such a thing to take over — a win, win for all concerned.
  • Go Long  

    1970-01-01 00:00

    Tim Bray: this generation of Republicans aren’t reasonable people with whom I have disagreements, they’re corrupt lying torturing malevolent buffoons. Short People got no reason Short People got no reason Short People got no reason To live Campaign high point and low point. They got little hands And little eyes And they walk around Tellin' great big lies They got little noses And tiny little teeth They wear platform shoes On their nasty little feet Lauren Wood: The bit I have found most disconcerting in the whole process, which has been going on for months now, is the demonisation of each side’s supporters, the assumption they’re not intelligent/patriotic/… enough. The level of vitriol hurled around is astounding, not only at the candidates themselves, but at their supporters Well, I don’t want no Short People Don’t want no Short People Don’t want no Short People Round here Apparently Democrats and Republicans agree that we need to privatize profit and socialize risk.  And that Habeas corpus is passé.  On the other hand, they do disagree on whether socialism starts at somewhere around 40% or at 35% marginal tax rates. Short people are just the same As you and i (a fool such as I) All men are brothers Until the day they die (its a wonderful world) Perhaps somebody should introduce Tim and Lauren. Short People got nobody Short People got nobody Short People got nobody To love Optimist view.  Realist view.  Until the conventions, I would have said that either presidential candidate would have done an adequate job, and certainly a better one than any of the three predecessors in that position.  Since then, the last two months have shown a clear difference between the two candidates. They got little baby legs That stand so low You got to pick em up Just to say hello They got little cars That go beep, beep, beep They got little voices Goin peep, peep, peep They got grubby little fingers And dirty little minds Theyre gonna get you every time Well, I dont want no short people Dont want no short people Dont want no short people round here — Randy Newman Now is a time for a bit of optimism; and a time for winning, or losing, gracefully.