Posts com o tag ‘Desenvolvimento de Software’

9ago2010

9 perguntas sobre o uso de Métodos Ágeis na Locaweb

(0) comentários

1 – Quando e como surgiu a idéia de implantar a XP como método de desenvolvimento?

Começamos a fazer XP em 2006, no time que desenvolvia o PABX Virtual (antiga Locaweb Telecom). Quem trouxe a ideia de XP foi Daniel Cukier, um dos desenvolvedores do time na época. Ele tinha cursado em seu mestrado (IME-USP) duas disciplinas sobre Métodos Ágeis e já era membro da Agilcoop. Já tinha adquirido certa experiência para tentar aplicar o aprendizado num projeto de verdade. O time era pequeno na época (4 desenvolvedores) e o gerente gostou bastante das ideias propostas no XP que lhes foram apresentadas. O time de Telecom era relativamente isolado dos outros times de desenvolvimento e isso facilitou as coisas. Era possível adotar a metodologia como um projeto piloto, sem afetar outras áreas ou produtos. Durante 8 meses, de outubro de 2006 até junho de 2007 o time se estabeleceu na metodologia. O produto PABX Virtual foi lançado e sua evolução em termos de funcionalidades era rápida e eficiente, com poucos bugs. A cada duas semanas, o time de Telecom lançava novidades no produto. Em paralelo a isso, Daniel Cukier e Maurício De Diana criaram um grupo de estudos de tecnologia, onde um dos assuntos estudados foi Métodos Ágeis. O grupo de estudos serviu para que as pessoas pudessem conhecer mais os detalhes não só de XP, mas também de outras metodologias como Scrum e Lean. As reuniões do grupo tinham entre 10 e 20 pessoas. Esse grupo de estudos tentou iniciar um projeto multi-equipes usando XP, mas devido às várias atribulações do dia-a-dia dos membros do grupo, o projeto não deu certo.Em meados de junho/julho de 2007 foram feitas algumas apresentações para a diretoria da empresa, com o objetivo de estimular a adoção de Métodos Ágeis em todos os times de desenvolvimento da empresa. Para convencer os diretores, foram mostrados números que demonstravam a qualidade do produto PABX, tanto em relação ao código quanto à velocidade de criação de novas funcionalidades. Na mesma época, um consultor internacional veio à empresa dar um curso para o time de produtos. Esse consultor comentou que gostava de Métodos Ágeis para desenvolver software. Isso foi a gota d’água que faltava.

No mês de agosto, a diretoria patrocinou um treinamento em Métodos Ágeis para desenvolvedores, gerentes e clientes internos de diversas áreas da empresa (até o presidente participou). A partir daí, iniciou-se um processo longo e trabalhoso de adoção das práticas Ágeis dentro da empresa. ler mais

30abr2010

1st International Workshop on Test-Driven Development (TDD 2010)

(2) comentários

No dia 11 de abril participei do 1st International Workshop on Test Driven Devevelopment (TDD2010), realizado em Paris. Fui apresentar meu paper entitulado “Most Common Mistakes in TDD Practice: Results From An Online Survey With Developers” (que pode ser baixado aqui). A plateia contava com pessoas de renome na comunidade como Michael Feathers, Steve Freeman, Laurie Williams, David Janzen, John Clements, entre outros.

O keynote foi feito pelo Steve Freeman, autor de um dos melhores livros de OO e TDD que já li (Growing Object-Oriented Software, Guided by Tests). Ele basicamente mostrou seu ponto de vista sobre TDD. Segundo ele, “programação comum está para TDD assim como programação procedural está para programação orientada à objetos”. Foco bem definido, bom feedback e progredir sem medo também foram citados como vantagens. Uma citação muito interessante, e que venho pensando há muito tempo sobre como transmitir essa idéia é a de que “o programador deve entender o porquê TDD funciona; caso contrário, é apenas burocracia”.

Em seguida, o aluno de mestrado Theodore Hellman nos apresentou a ferramenta que seu grupo desenvolve em Calgary, Canadá. Ela testa interfaces de uma maneira muito interessante: antes de desenvolver cada interface, o programador faz protótipos na ferramenta, desenhando caixas de texto, botões (parecidos com os que desenhamos no papel). A mágica acontece depois que o protótipo está pronto: você testa o protótipo, clicando nos botões e digitando valores nas caixas de texto fictícias, e a aplicação os executa na aplicação real! O problema é que funciona apenas para aplicações .NET Windows Forms.

Em seguida a apresentação do John Clements (um dos criadores do Dr. Scheme) e do David Janzen (professor da Politecnica da California, possui muitas publicações relacionadas a experimentos com TDD) sobre o quão difícil é ensinar TDD para os alunos. Apresentaram algumas técnicas de ensino e problemas que já tiveram nas primeiras aulas sobre o assunto.

O alemão Florian Barth, da Universidade de Manheim, mostrou a ferramenta para testes de aceitação que seu grupo trabalha. É uma mistura de Fitnesse com linguagem de programação, onde você escreve não só os casos de testes como a implementação do teste em planilhas. É bem interessante, já que basicamente elimina o trabalho de codificação dos testes. O problema é que algumas coisas ainda são um pouco complicadas e exigem um trabalho extra na planilha. Mas sem dúvida é um projeto para ficar de olho.

Robert Chatney apresentou seu framework LiFT, uma DSL para testes de aceitação em Java, inspirada no Cucumber. Ela faz com que seus testes em Java fiquem muito fluentes. Totalmente extensível, é um projeto que pretendo colaborar em breve. O código pode ser encontrado no Google Code.

Em seguida, apresentei meu paper sobre erros (ou desvios) que os programadores cometem quando praticam TDD. Apesar de algumas críticas em relação à metodologia (problemas esses que já eram conhecidos e estavam na seção de “ameaças a validar” do paper), as ideias ali foram elogiadas e todos concordaram com os problemas levantados pelo artigo. Lembrando que esse artigo é apenas um trabalho em andamento, parte da minha dissertação de mestrado. Espero postar partes dela em breve.

Chris Agmen-Smith apresentou sua experiência em um projeto real com ATDD, e fez questão de mostrar que é possível fazer ATDD sem grandes custos, mas com muitos benefícios.

No final do dia, Raj Mudhar solicitou ajuda para pesquisar na área de ATDD em projetos de grande porte. Qualquer empresa que tenha projetos grandes pode participar, divulgando seus dados e juntos publicarem os resultados em conferências de peso.

Resumindo, o evento foi muito muito bom. Um dia inteiro com riquíssimas discussões sobre as mais variadas pesquisas em TDD. Além disso, pude validar muitas ideias com pessoas realmente influentes na área. Um ponto muito interessante é que não estamos tão longe do que eles praticam no dia-a-dia, mas ainda temos um longo caminho a percorrer!

Até a TDD2011, em Berlin! :)

O post original pode ser encontrado em http://www.aniche.com.br/2010/04/1st-international-workshop-on-tdd-2010/

16abr2010

TDD realmente ajuda?

(0) comentários

Geralmente um programador que nunca praticou TDD tem essa dúvida: será que TDD realmente ajuda na qualidade do código? E na redução de defeitos? Ele aumenta ou diminui a produtividade, afinal? Mas como toda e qualquer prática em engenharia de software, é muito difícil avaliar e chegar a uma conclusão exata sobre seus ganhos e benefícios.

Nos últimos anos, a comunidade acadêmica vem rodando diversos experimentos para tentar mostrar de maneira empírica que TDD realmente ajuda no processo de desenvolvimento de software. Alguns desses estudos são feitos por professores bastante conhecidos na comunidade, como a prof. Laurie Williams (North Carolina State University) e o prof. David Janzen (California Polytechnic State University).

Algumas dessas pesquisas investigam o fato de TDD reduzir o número de defeitos de um software; já outros investigam o fato de TDD produzir código de melhor qualidade. Alguns até pesquisam por indícios de aumento de produtividade.

Estudos na indústria

Janzen [5] mostrou que programadores usando TDD na indústria produziram código que passaram em aproximadamente 50% mais testes caixa-preta do que

o código produzido por grupos de controle que não usavam TDD. Além do mais, o grupo que usava TDD gastou menos tempo debugando. Janzen também mostrou que a complexidade dos algoritmos era muito menor e a quantidade e cobertura dos testes era maior nos códigos escritos com TDD.

Um estudo feito pelo Maximillien e Williams [6] mostrou uma redução de 40-50% na quantidade de defeitos e um impacto mínimo na produtividade quando programadores usaram TDD.

Outro estudo feito por Lui e Chan [7] comparando dois grupos, um utilizando TDD e o outro escrevendo testes apenas após a implementação, mostrou uma redução significante no número defeitos. Além do mais, os defeitos que foram encontrados eram corrigidos mais rapidamente pelo grupo que utilizou TDD. O estudo feito por Damm, Lundberg e Olson [8] também mostra uma redução significante nos defeitos.

O estudo feito por George e Williams[9] mostrou que, apesar de TDD poder reduzir inicialmente a produtividade dos desenvolvedores mais inexperientes, o código produzido passou entre 18% a 50% mais em testes caixa-preta do que códigos produzidos por grupos que não utilizavam TDD. Esse código também apresentou uma cobertura entre 92% a 98%. Uma análise qualitativa mostrou que 87.5% dos programadores acreditam que TDD facilitou o entendimentos dos requisitos e 95.8% acreditam que TDD reduziu o tempo gasto com debug. 78% também acreditam que TDD aumentou a produtividade da equipe. Entretanto, apenas 50% acreditam que TDD ajuda a diminuir o tempo de desenvolvimento. Sobre qualidade, 92% acreditam que TDD ajuda a manter um código de maior qualidade e 79% acreditam que ele promove um design mais simples.

Nagappan [12] mostrou um estudo de caso na Microsoft e na IBM e os resultados indicaram que o número de defeitos de quatro produtos diminuir entre 40% a 90% em relação à projetos similares que não usaram TDD. Entretanto, o estudo mostrou também TDD aumentou o tempo inicial de desenvolvimento entre 15% a 35%.

Langr [10] mostrou que TDD aumenta a qualidade código, provê uma facilidade maior de manutenção e ajuda a produzir 33% mais testes comparados a abordagens tradicionais.

Estudos na academia

Um estudo feito por Erdogmus et all [11] com 24 estudos de graduação mostrou que TDD aumenta a produtividade. Entretanto nenhuma diferença de qualidade no código foi encontrada.

Outro estudo feito por Janzen[13] com três diferentes grupos de alunos (cada um deles usando uma abordagem diferente: TDD, test-last, sem testes), mostrou que o código produzido pelo time que fez TDD usou melhor conceitos de orientação a objetos e as responsabilidades foram separadas em 13 diferentes classes enquanto que os outros times produziram um código mais procedural. O time de TDD também produziu mais código e entregou mais features. Os testes produzidos por esse time teve duas vezes mais asserções que os outros e cobriu 86% mais branches do que o time test-last.  Além do mais, as classes testadas tinham valores de acoplamento 104% menor do que as classes não testadas e os métodos eram, na média, 43% menos complexos do que os não-testados.

O estudo de Müller e Hagner [17] mostrou que TDD não resulta em melhor qualidade ou produtividade. Entretanto, os estudantes perceberam um melhor reuso dos códigos produzidos com TDD.

Steinberg [15] mostrou que código produzido com TDD é mais coeso e menos acoplado. Os estudantes também reportaram que os defeitos eram mais fáceis de serem corrigidos.

O estudo do Edwards [16] com 59 estudantes mostrou que código produzido com TDD tem 45% menos defeitos e faz com que o programador se sinta mais a vontade com ele.

Conclusão

A maioria dos experimentos feitos tanto na indústria quanto na academia mostram que TDD melhora o processo de desenvolvimento de software, aumentando a qualidade do código, reduzindo o número de defeitos, diminuindo o tempo gasto com depuração e até aumentando a produtividade dos desenvolvedores.

Entretanto, mais experimentos devem ser conduzidos, levando em consideração diferentes fatores de influência que existem em um ambiente de desenvolvimento de software.

Referências

Podem ser encontradas aqui.

Post original em http://www.aniche.com.br/2010/04/tdd-realmente-ajuda/

7abr2010

Utilizando debug em aplicações Rails

(0) comentários

Muita gente acha (assim como eu achava há um tempo atrás) que não é possível utilizar debug (ou “depurar”, ou “debugar”) uma aplicação Ruby on Rails. Acredito que isso se deve ao fato de Ruby ser uma linguagem interpretada, diferente de outras linguagens que requerem compilação.

Existe uma forma muito simples de se “debugar” em Rails. Primeiro vamos instalar a gem ruby-debug.

$ sudo gem install ruby-debug

Agora vamos criar uma aplicação Rails de exemplo para utilizar o debug.

$ rails prodis-debug
$ cd prodis-debug
$ rails script/generate scaffold MyModel my_text:string my_value:float
$ rake db:migrate

E ao invés de subir a aplicação com script/server, iniciamos a aplicação em modo debug:

$ rdebug script/server
/Users/Prodis/Blog/code/prodis-debug/script/server:2
require File.expand_path('../../config/boot',  __FILE__)
(rdb:1)

Esse é o momento de adicionarmos o(s) breakpoint(s) desejados, informando o arquivo e a linha do mesmo. Vamos colocar um breakpoint na ação create de MyModelsController:

(rdb:1) break app/controllers/my_models_controller.rb:43
Breakpoint 1 file /Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb, line 43

E usamos o comando cont para prosseguir com o carregamento da aplicação no servidor.

(rdb:1) cont
=> Booting Mongrel
=> Rails 2.3.5 application starting on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server

No browser, através do endereço http://localhost:3000/my_models/new, vamos criar um novo registro de MyModel.

Quando clicarmos no botão “Create”, a aplicação irá parar no breakpoint que colocamos na linha 43 de MyModelsController.

Breakpoint 1 at /Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb:43
/Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb:43
@my_model = MyModel.new(params[:my_model])
(rdb:6) 

A partir daqui podemos investigar o código e ter acesso as suas variáveis. Por exemplo, exibindo os valores da variável params:

(rdb:6) p params
{"my_model"=>{"my_text"=>"I am some text", "my_value"=>"50.00"},
"commit"=>"Create", "authenticity_token"=>"qN9mUhJelZD0V/P2CB1fhBkQrIxFvvk+NRkY5m6EFWg=",
"action"=>"create", "controller"=>"my_models"}

Mas a maneira mais fácil de inspecionar variáveis é pelo irb, que irá se manter no contexto atual onde a aplicação está parada.

(rdb:6) irb
irb(#):001:0> params
=> {"my_model"=>{"my_text"=>"I am some text", "my_value"=>"50.00"},
"commit"=>"Create", "authenticity_token"=>"qN9mUhJelZD0V/P2CB1fhBkQrIxFvvk+NRkY5m6EFWg=",
"action"=>"create", "controller"=>"my_models"}
irb(#):002:0>

Saindo do irb (com o comando exit), nós voltamos para o ambiente de debug. Há uma série de comandos úteis para se utilizar. Um deles é o list, que exibe o trecho de código onde a aplicação está parada e indicando sua linha exata através de uma seta:

(rdb:6) list
[38, 47] in /Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb
38    end
39
40    # POST /my_models
41    # POST /my_models.xml
42    def create
=> 43      @my_model = MyModel.new(params[:my_model])
44
45      respond_to do |format|
46        if @my_model.save
47          flash[:notice] = 'MyModel was successfully created.'
(rdb:6)

Para avançar até a próxima linha, usamos o comando next:

(rdb:6) next
/Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb:45
respond_to do |format|
(rdb:6)

Para listar todos os comandos disponíveis, basta entrarmos no help:

(rdb:6) help
ruby-debug help v0.10.3
Type 'help ' for help on a specific command
Available commands:
backtrace  delete   enable  help    next  quit     show    trace
break      disable  eval    info    p     reload   source  undisplay
catch      display  exit    irb     pp    restart  step    up
condition  down     finish  list    ps    save     thread  var
continue   edit     frame   method  putl  set      tmate   where 
(rdb:6)

Também podemos visualizar a ajuda de um comando específico:

(rdb:6) help next
n[ext][+-]?[ nnn]	step over once or nnn times,
'+' forces to move to another line.
'-' is the opposite of '+' and disables the force_stepping setting.
(rdb:6) 

E para continuar a execução da aplicação, usamos o comando continue (ou um dos seus álias c ou cont)

(rdb:6) continue
Processing MyModelsController#create (for 127.0.0.1 at 2010-03-20 12:20:54) [POST]
Parameters: {"my_model"=>{"my_text"=>"I am some text", "my_value"=>"50.00"},
"commit"=>"Create", "authenticity_token"=>"qN9mUhJelZD0V/P2CB1fhBkQrIxFvvk+NRkY5m6EFWg="}
MyModel Create (0.5ms)   INSERT INTO "my_models" ("my_text", "created_at", "updated_at", "my_value")
VALUES('I am some text', '2010-03-20 15:45:19', '2010-03-20 15:45:19', 50.0)
Redirected to http://localhost:3000/my_models/4
Completed in 1464995ms (DB: 1) | 302 Found [http://localhost/my_models]

Existe também uma outra maneira de se “debugar” com a gem ruby-debug. Nós podemos colocar uma instrução debugger (que na verdade é um método) em qualquer lugar do código para marcar como breakpoint. O Railscast #54 Debugging with ruby-debug mostra os passos de como fazer isso.

Eu particularmente gosto de configurar os breakpoints direto no Terminal, evitando assim ter que colocar qualquer informação de debug no código.

Esse esquema de debug não se restringe somente à aplicações Rails. Você pode muito bem utilizá-lo com código Ruby diretamente. Por exemplo, o código abaixo é do arquivo some_class.rb:

#lib/some_class.rb
class SomeClass
  def some_method
    # do something here
  end

  def other_method
    # do another thing here
  end
end

É só utilizar o comando rdebug passando o nome do arquivo:

$ rdebug lib/some_class.rb
/Users/Prodis/Blog/code/prodis-debug/lib/some_class.rb:2
class SomeClass
(rdb:1) 

Para informações mais detalhadas sobre a gem ruby-debug, veja a documentação completa.


Post original:
http://prodis.pro.br/2010/03/20/utilizando-debug-em-aplicacoes-rails

.

22mar2010

Métodos que retornam mais de um valor em Ruby

(0) comentários

Nas últimas semanas, a equipe que eu trabalho estava desenvolvendo um web service onde havia a necessidade de renderizar o retorno de uma lógica de negócio em representações XML. Digo representações (no plural), pois para um retorno com sucesso a representação seria uma e para retorno com erro a representação seria outra.

Por exemplo, um retorno com sucesso:

<natural-person>
   <name>Prodis</name>
   <cpf>01234567890</cpf>
</natural-person>

E um retorno sem sucesso:

<error>
   <description>CPF inválido.</description>
</error>

Como era um web service em REST, para sucesso retornamos o código de status HTTP “200 OK” e, dependendo do não sucesso da operação, o código de status HTTP da resposta poderia ser “400 Bad Request”, “404 Not Found” ou qualquer outro código 4xx ou 5xx que melhor se adequasse.

Mantendo um controller magro, a idéia era somente instanciar uma classe de negócio, chamar um método e renderizar o retorno. Algo como:

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    natural_person = business.search_by_cpf params[:cpf]

    # TODO: Renderizar pessoa física ou mensagem de erro
  end
end

A partir daqui a equipe iniciou uma discussão sobre a melhor forma de se obter o(s) retorno(s) esperado(s). O controller precisava saber se a consulta havia sido feita com sucesso, para renderizar um objeto NaturalPerson retornando o código de status HTTP 200, ou se a consulta não tivesse sucesso, renderizar a mensagem de erro retornando um código de status HTTP 4xx adequado.

Como “bons programadores .NET e Java”, a primeira coisa que pensamos foi lançar uma exceção customizada caso a consulta não tivesse sucesso, capturar essa exceção no controller e, através das informações de descrição de erro e código de status HTTP contidas nessa exceção, renderizar o retorno adequado.

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new

    begin
      natural_person = business.search_by_cpf params[:cpf]

      render natural_person, 200
    rescue BusinessException => e
      render e.error, e.status_code
    end
  end
end

A gente não tinha visto muito código Ruby utilizando begin rescue, então essa solução não nos pareceu muito “Ruby way”. Achamos melhor pedir a opinião de alguém com mais experiência em Ruby. Perguntamos ao Rafael Rosa, que nos disse que cada vez que lançamos uma exceção em Ruby “alguma coisa ruim acontece no servidor” e consequentemente a aplicação ficará mais lenta.

Ele indicou um post falando a respeito:
http://www.simonecarletti.com/blog/2010/01/how-slow-are-ruby-exceptions

Rafael Rosa nos sugeriu retornar um array de duas posições: uma com o código de status HTTP e outra com o objeto a ser renderizado.

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    result = business.search_by_cpf params[:cpf]

    render result[1], result[0]
  end
end

Resolveu, mas o código não ficou muito intuitivo. A partir daí imaginamos algumas outras soluções.

Retornar um hash:

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    result = business.search_by_cpf params[:cpf]

    render result[:data], result[:status_code]
  end
end

Criar uma classe de retorno:

class SomeBusinessResult
  attr_accessor :status_code, :data
end
class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    result = business.search_by_cpf params[:cpf]

    render result.data, result.status_code
  end
end

Posteriormente, o Rafael Rosa também sugeriu essa última opção, mas criando uma Struct ao invés de uma classe.

Então eu sugeri o método search_by_cpf retornar dois valores. Todos da equipe me perguntaram: “Como assim retornar dois valores?”. Falei que em Ruby um método pode retornar vários valores, que vi isso no livro The Ruby Programming Language.

O código do controller ficou bem mais intuitivo:

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    status_code, data = business.search_by_cpf params[:cpf]

    render data, status_code
  end
end

O método search_by_cpf está retornando tanto o código de status HTTP quanto os dados para serem renderizados:

class SomeBusiness
  def search_by_cpf(cpf)
    # Lógica de negócio aqui
    return 200, natural_person
  end
end

Note que mesmo a linha 4 sendo a última linha de instrução do método, o retorno de mais de um valor obrigatoriamente precisa utilizar o comando return.

Quando há mais de um valor de retorno para um método, os valores são colocados implicitamente dentro de uma array e essa array fica sendo o único retorno do método.

O mesmo resultado seria obtido dessa forma:

class Business
  def search_by_cpf(cpf)
    # Lógica de negócio aqui
    [200, natural_person]
  end
end

Quem está consumindo um método que retorna mais de um valor, pode utilizar o recurso de atribuição paralela do Ruby para distribuir os valores de retorno em variáveis distintas, como é o caso no nosso controller:

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    status_code, data = business.search_by_cpf params[:cpf]

    render data, status_code
  end
end

O dinamismo do Ruby lhe oferece várias opções para você encontrar soluções para o mesmo problema ou questão. Cabe a você decidir qual melhor abordagem para seu tipo de problema. O interessante é você conhecer essas opções para facilitar a sua decisão.


Post original:
http://prodis.pro.br/2010/02/20/metodos-que-retornam-mais-de-um-valor-em-ruby

.