Páginas

Friday, December 19, 2008

Alguns achados na web...

Encotrei um site muito legal!
Mas muito mesmo... é um agregador de feeds que junta os posts/etc mais recentes dos melhores (?) sites de diversas categorias.

Para todos nós sem tempo de fazer qualquer coisa, é uma boa pedida. O engraçado é como inventamos soluções para resolver problemas surgidos das soluções que demos para outros problemas...

Inventamos os feeds para nos polpar tempo, mas eles começaram a ser muitos e nos tomar tempo lendo cada coisa interessante. Daí os agregadores, as recomendações personalizadas, ...

Bem, chega de papo furado. O site que encontrei é o Alltop:

http://alltop.com

E a partir dele, na categoria programming, encontrei um post muito legal sobre bugs e Scrum. Fala sobre como abordar a inevitável existência de bugs, e como enquadrar isso dentro do sprint.
Nos mesmo site, tem um post sobre um vídeo explicando Scrum em menos de 10 minutos!

São eles:

http://agilesoftwaredevelopment.com/blog/janusz-gorycki/stories-battlefield-2-my-god-its-full-bugs
e
http://agilesoftwaredevelopment.com/blog/artem/scrum-under-10-minutes-video

Também vale a pena olhar a categoria de user interfaces.

Tuesday, December 16, 2008

Domingo de treinamento Scrum

Continuando meu relato sobre o treinamento de Scrum que tivemos na globo.com neste final de semana, agora vou falar do segundo dia.

No fim do sábado estávamos falando sobre os Papéis de Scrum, e foi esse tema que iniciou a manhã de domingo. Já no sábado tivemos bastante discussão sobre como isso estava sendo feito na globo.com e como acreditamos que deveria ser.


A problemática está em torno de que os papéis do processo não são como posições ou cargos na empresa. Pelo que pude perceber com o pouco tempo de empresa e com o contato com o pessoal que participou do treinamento, isso não está tão claro para todos os times. Muitos Scrum Masters eram gerentes no processo cascata, e não aderiram a Agilidade. Vestem a roupa do Scrum mas continuam empurrando o processo.

Isso vai contra a idéia do processo/sistema puxado vs. empurrado. No empurrado, as pessoas são designadas a cumprir tarefas, tem a estrutura hierárquica forçando o funcionamento do sistema, enquanto que no puxado, cada um assume a responsabilidade que lhe cabe.

Uma analogia industrial seria falar de uma fábrica em que uma máquina recebe como entrada matéria-prima e retorna algo processado para seguir para uma nova máquina. As máquinas não estão em sincronia, e se a segunda tem capacidade de trabalho menor que a primeira, teremos a formação de um gargalo.
No processo puxado, a última máquina, quando livre, solicita insumo da anterior, que recursivamente vai pedindo material da máquina que lhe precede.

Retomando os papéis, no Scrum temos o Scrum Master, o Product Owner e a Team/Equipe.
As responsabilidades são as seguintes:
  • Scrum Master
    • Cuidar da Equipe
    • Trabalhar com o Product Owner
    • Revomer impedimentos
    • Manter o processo funcionando
    • Socializar Scrum na Organização
  • Equipe
    • Estimar o tamanho dos itens do Backlog (conjunto de tarefas a serem realizadas)
    • Assumir compromisso com a entrega de um incremento de software funcional
    • e entregá-lo
    • Manter o seu próprio progresso (com o auxílio do Scrum Master)
    • Auto-organizados, responsáveis pela entrega daquilo prometido para o Product Owner
  • Product Owner
    • Trabalhar numa visão compartilhada
    • Levantar requisitos
    • Gerenciar e priorizar o Product Backlog
    • Aceitar o Software no final de cada interação
    • Gerenciar o Release Plan
    • Responsável pela lucratividade do projeto (ROI)

Ontem, segunda-feira, tive a oportunidade de conversar com um amigo, o Bruno Nascimento, que é gerente numa empresa de desenvolvimento, sobre essa problemática de quem deve assumir cada responsabilidade. Ele me contou que ele tem que conferir e homologar cada código que um desenvolvedor gera, num trabalho manual e centralizado em suas mãos.

Quer dizer que a responsabilidade do código não está na mão da equipe, e sim do gerente. Esse formato acaba causando o efeito "mijada", quando o gerente é cobrado por um software bugado, e ele então passa a culpa para os seus subordinados (a mijada descendo as escadas...), e por aí vai até chegar no pobre coitado estagiário que estava lá trabalhando insatisfeito, fazendo coisas por obrigação, mas que não interferiam diretamente na vida dele (a não ser pela já esperada mijada).

Essa situação pode ser diferente se a responsabilidade de entregar código testado for da equipe. Eles vão se certificar de fazer seu melhor, e sabem que são os responsáveis diretos pela falha. O problema é que estamos sempre procurando alguém para culparmos, e esse alguém sempre precisa de outro alguém para passar a culpa e tirar o seu da reta... É um processo defensivo, não jogamos para ganhar, e sim para esconder o fracasso que estamos temendo. Devemos jogar para ganhar!

Finalizada a discussão sobre os papéis e como colocar na cabeça dos que não querem ter seu queijo mexido que não podemos continuar com a mentalidade anterior, fizemos uma lista dos impedimentos mais comuns que temos no nosso dia-a-dia. E seguimos falando de como implementar o Scrum, das reuniões de planejamento (Estimation Meeting, Planning Meeting, Daily Meeting, Sprint Review).


Foi o momento para falar do conceito de "done" ou "pronto". Já vi discussões sobre isso em diversas listas sobre Agile, nacionais ou internacionais, e o que acaba acontecendo é que cada um tem uma opinião e que funciona. Alguns consideram pronto quando a equipe termina as tarefas da história, outros quando o P.O. aceita, outros quando está em produção, etc.
O importante é ter um conceito bem definido e difundido dentro da equipe, de forma que todos saibam qual é o objetivo, onde "parar" pois o software já está hmm.... pronto!

Fizemos uma atividade de escrever User Stories (estórias) para um Terminal de Auto-Atendimento, identificando o tipo de usuário a que se destina a estória, a funcionalidade e o benefício que esta traz.



As estórias são "lembretes para um conversa futura". Surgiu com o pessoal de Extreme Programming (XP), como forma de indicar o que o software deve fazer. Seria, para os que querem uma comparação, uma forma simples de declarar os casos de uso.

Então, separados em 4 equipes, escremos os cartões (estórias...) com o que nossos terminais deveriam ter/fazer e, em seguida, cada grupo apresentou suas idéias e como cada uma delas funcionava do ponto de vista do usuário, que benefício ele teria caso aquele estória fosse implementada.

Chegou então o momento em que falamos sobre planejar. No XP sabemos que mais vale o ato de planejar do que seguir um plano (que pode se mostrar incoerente com a realidade).
O Juan Bernabó fez essa distinção através de duas outras palavras: estratégia e tática.
Usou o exemplo de uma guerra: um geral pode definir a estratégia de um ataque anos antes do mesmo ser realizado. A estratégia é o "pra que?". A razão de invadir o país pode ser "para conquistar território", para "lutar contra o terror", etc...

Agora a tática, o "como?", se definida 5 anos antes do ataque, só garante uma coisa: o fracasso. Em pouco tempo o campo de batalha pode mudar drasticamente. Fontes de energia podem ser construídas, bases militares mudam de local, sistemas de comunicação mudam, enfim, não podemos prever como será um país com anos de antecedência... nem mesmo meses..., se o ataque fosse mesmo acontecer, a tática seria definida o mais tarde possível. Vem então mais uma idéia do Lean, de evitar tomar decisões cedo, tomá-las sempre no último momento possível.

Posteriormente falamos das estimativas, e do Planning Poker. Em equipe, estimamos pontos para nosso projeto de caixa eletrônico. Neste momento surgiu alguma dificuldade em acertar o valor relativo que a equipe definia para cada item. Com a ajuda do Juan conseguimos entrar num consenso, poderiam jogar valores sem nos preocupar tanto e ir ajustando os pesos de cada item ao longo das outras estimativas.

Então foi mais ou menos assim: pegamos uma estória por vez, e para aquela história colocávamos na mesa uma carta do Planning Poker, como o valor que julgávamos apropriado (0, 1/2, 1, 2, 3, 5, 8, 13, 20, 40, 100 ou "?"). Se o conseso fosse imediato, o a complexidade da estória estava definida (lembrando que não estimamos tempo...). Caso houvesse disparidade, os extremos explicavam porque colocaram um valor mais baixo ou alto e então novamente jogávamos as cartas, visando o consenso.


Por fim, para terminar o treinamento, executamos 2 sprints no "Jogo da Velocidade", que nada mais era que o XPGame com um nome diferente!
Foi legal trabalhar em equipe, conhecer pessoas novas e trocar idéias.

Do sábado para o domingo, assisti a um vídeo da palestra do Bardusco sobre a implantação do Scrum na globo.com, que me ajudou a entender melhor "aonde estamos". Já havia lido sobre a transição nos diversos blogs, mas ouvir o Bardusco pareceu mais interessante.

Enfim, um final de semana muito bom! (Enquanto meus amigos da universidade se divertiam em Arraial do Cabo... haha)

Saturday, December 13, 2008

O que me espera em 2009?

Assisti a apresentação do Danilo Bardusco na Falando Em Agile sobre a implantação do Scrum na globo.com.
O post original está aqui: http://blog.bardusco.com/2008/10/25/falando-em-agile-2008/

Lá pelos 32 minutos de vídeo, ele começa a contar que o foco para 2009 são práticas de engenharia, como Pair Programming, TDD, deploy automático, etc.
O Labase foi um lar por me dar toda essa base, e será muito bom poder implementar isso na globo.com, trabalhando para milhões de usuários, e fazendo trabalho de qualidade em um ambiente muito agradável.

Uma semana de globo.com e Dinâmicas Ágeis (Scrum)

Na quinta-feira completei 1 semana na globo.com!
Estou muito feliz com o novo ambiente de trabalho, sem largar o Labase no NCE/UFRJ.

A propósito, a experiência adquirida durante um ano e meio no Labase tem sido muito relevante em minha vida e carreira. Como se não bastasse Agile no ambiente de trabalho, sempre me identifiquei com as metodologias a ponto de aplicar diversas coisas na minha vida pessoal.

Lá no Labase nós somos uma equipe de Extreme Programming (XP), porém meus estudos na área de metodologias de desenvolvimento e gerenciamento de projetos sempre foi com uma visão mais abrangente, de forma que fui procurar entender os métodos tradicionais, o waterfall, o RUP, e outras "estranhezas sem sentido". E no mundo ágil, apesar de simpatizar totalmente com o XP, e encarar as práticas técnicas que assustam muitos desenvolvedores, também acabei vendo o Scrum, que tem sido melhor vendido no Brasil se comparado ao XP.

Tudo isso para tentar entender melhor como funciona da dinâmica atual do mercado, porque o modelo tradicional é o que é, porque precisamos mudar a mentalidade, e como eu posso impactar positivamente essa mudança de paradigma.

O marketing do Scrum tem sido mais feliz, parece que XP é extremo demais para pessoas normais... E lá na globo.com nós seguimos o Scrum. Bem, como pode ser constatado em outros blogs, entamos em processo de implantação do Scrum, de mudança da mentalidade de muita gente. Alguns compram a idéia do Agile e fazem funcionar, outros já ficam com medo e acabam querendo proteger seu trabalho a todo custo... o medo de evoluir, ou de ser descartado... vai entender.

O fato é que a globo.com tem investido pesado em treinamentos e consultorias. E trabalhamos com o que há de melhor no mercado, com o pessoal da Teamware, na maioria das vezes representada pela pessoa do Juan Bernabó. Eu conheci o Juan numa palestra em agosto.
Apesar do pouco tempo de empresa, já estou participando de um desses treinamentos neste final de semana. O Juan realizou conosco algumas dinâmicas, e pudemos conversar, discutir diversos aspectos aplicados à nossa realidade.

Voltando a experiência no Labase, foi lá que me deparei pela primeira vez com o mundo Ágil, e me apaixonei. Quem conversa comigo talvez já tenha ouvido meu relato de que a cada instante que aprendo mais eu falo "ué, e não é assim que funciona a engenharia de software tradicional?!"... ou coisas do tipo...

E no Labase não ficamos só no papel, nós procurarmos vencer as dificuldades (muitas vezes impostas por ainda estarmos cursando nossa graduação), e tive o grande prazer de nos últimos meses estar numa posição de liderança que muito agregou.

Como contei em outro post, nós realizamos no NCE a dinâmica do XPGame em uma versão adaptada para os alunos do mestrado, e pretendemos fazer nessa semana (agora com data marcada!) uma dinâmica da Fábrica de Aviões.

Bem, hoje no treinamento da Teamware adicionei em minha bagagem mais dois itens interessantes. Primeiro nós realizamos uma atividade de criação de um folder para marcianos.
Era uma atividade introdutória, muito relevante para perceber que precisamos ter muito claro o conceito de "pronto" (quando uma tarefa está concluída), o foco no que o cliente quer, trabalhar no produto entregável que traz valor para o cliente ao invés de gerar especificações, etc.

A outra dinâmica foi a das bolas de tênis. Tínhamos um conjunto de bolas, e cada bola que passasse na mão de todos os participantes e voltasse para o primeiro contaria um ponto. O objetivo era obter o maior número de pontos possíveis no time-box de 2 minutos.
A restrição é que as bolas não poderiam ser passadas para um pessoa imediatamente ao seu lado de forma conduzida.

Fizemos seis sprints, e em cada um deles tínhamos que estimar um mínimo e um máximo para o número de pontos que conseguiríamos entregar. Como de se esperar, a primeira estimativa foi muito difícil, pois não fazíamos idéia da complexidade da tarefa. Estimamos entre 5 e 10 bolas.
Entregamos 12 bolas, passando as bolas em zigue-zague num corredor de pessoas e o último lançava a bola de volta ao primeiro para completar o ponto.

Vimos que poderíamos melhor bastante, e de fato melhoramos, chegando a entregar 87 pontos no quinto sprint. Tudo fruto de trabalho em equipe, de uma equipe auto-gerida e que cuidou a cada interação da melhoria da sua forma de trabalhar, de seu processo. Diferente do que conhecemos como "mundo real", não tem uma pessoa de fora de dizendo como fazer algo no qual você é especialista. Não existe o papel do gerente ou que quer seja ditando como você deve fazer seu trabalho.

Esta atividade foi citada diversas vezes durante a apresentação do Scrum e dos papéis no Scrum, e junto a toda discussão tiramos algumas dúvidas de como aplicar tudo aquilo em nossas equipes, como fazer a metodologia funcionar de verdade.

Foi um sábado muito proveitoso, e amanhã tem mais!

Friday, December 5, 2008

Instalando e usando o Capistrano no Ubuntu Linux - Parte 2

Depois de instalarmos o Capistrano hoje pela manhã [1], chegou a hora de usar!

No momento não estamos desenvolvendo em Rails, o framework que tornaria o uso do Capistrano natural. Porém, o Capistrano não é limitado a framework ou linguagem.
Então, vamos configurar o deploy automático de uma aplicação web em TurboGears / Python.

Primeiro criamos um projeto TG básico:
$ tg-admin quickstart
Enter project name: lifeatmymind
Enter package name [lifeatmymind]:
Do you need Identity (usernames/passwords) in this project? [no]
...
$ cd lifeatmymind/
Criamos o diretório config só para termos um dos arquivos que o Capistrano irá criar:
$ mkdir config
E rodamos o comando para preparar o Capistrano para fazer deploy da aplicação: capify.
$ capify .
[add] writing `./Capfile'
[add] writing `./config/deploy.rb'
[done] capified!

Infelizmente tivemos que interromper nossa tarde por alguns problemas.
Ainda faltam alguns passos para efetivamente colocarmos o Capistrano em ação:
  • Configurar nosso repositório SVN
  • Configurar o apache2 (ou instalar o nginx)
  • Configurar o Capistrano
  • Ver se tudo funciona :D
Por hora, existe um tutorial oficial que pode dar uma noção do que fazer [2].

[1] http://lifeatmymind.blogspot.com/2008/12/instalando-e-usando-o-capistrano-no.html
[2] http://www.capify.org/getting-started/from-the-beginning

Síntese de voz usando Python

Na apresentação que fiz com o Neno na PythonCampus no dia 6 de novembro [1], mostramos um script para falar para a platéia. A autoria é do Neno, eu apenas pedi para publicar.

Uma pena que só funciona em Windows (depende do comtypes [2]). Para rodar em linux poderíamos usar o eSpeak, o mesmo sintetizador usado pelo Orca.

# -*- coding: utf-8 -*-
import comtypes.client

tts = comtypes.client.CreateObject("sapi.SPVoice")

frase1 = (u"Oi comunidade Python! Este é um exemplo do Python utilizando sintetizador de voz. "
u"É possível desenvolver programas com fala para cegos utilizarem o computador em Python.")
print frase1
tts.Speak(frase1)

v = tts.GetVoices()
totalDeVoz = len(v)

frase2 = u"Este computador tem %d vozes instaladas, são elas:" % totalDeVoz
print frase2
tts.Speak(frase2)

#pega o nome da voz de numero 0
nomeVoz = v[0].GetDescription()
#Lista o nome de todas as vozes instaladas
for i in v:
print i.GetDescription()

#Sintetiza o nome de todas as vozes instaladas
for i in v:
x = tts.Speak(i.GetDescription())



[1] http://lifeatmymind.blogspot.com/2008/11/palestra-na-python-campus.html
[2] http://downloads.sourceforge.net/comtypes/comtypes-0.5.2.win32.exe

Instalando e usando o Capistrano no Ubuntu Linux - Parte 1

Hoje resolvemos testar o Capistrano [1] como nova solução para fazer deploy automático das nossas aplicações web do Labase [2].
Partindo de um Ubuntu 8.04 sem o Ruby [3] instalado, o procedimento é o seguinte:

$ sudo apt-get install ruby irb rdoc rubygems

ou

Sistema > Administração > Synaptic
E procure os pacotes ruby, irb, rdoc e rubygems, marque para instalação e clique em "aplicar" (botão verde).

Agora podemos usar o RubyGems para instalar o Capistrano.
O site do Capistrano é enfático em dizer que basta uma linha.
No terminal, digite:

$ sudo gem install capistrano

Bem, isso deveria ter funcionado! Mas não deu certo, pois o pacote RubyGems do repositório do Ubuntu vem dos mantenedores do Debian, e está numa versão muito antiga (0.9.4). Precisamos da versão 1.3.1, que instalamos assim [4]:

$ wget http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz
$ tar xzf rubygems-1.3.1.tgz
$ cd rubygems-1.3.1
$ sudo ruby setup.rb
$ gem -v
bash: gem: comando não encontrado

Mas...

$ gem1.8 -v
1.3.1

O que aconteceu é que o rubygems instalou o script em /usr/bin/gem1.8, e não /usr/bin/gem como esperado. Então, basta criar um link simbólico:

$ sudo ln -s /usr/bin/gem1.8 /usr/bin/gem
$ gem -v
1.3.1

Com isso, agora conseguiremos instalar o Capistrano:
$ sudo gem install capistrano
Successfully installed net-ssh-2.0.4
Successfully installed net-sftp-2.0.1
Successfully installed net-scp-1.0.1
Successfully installed net-ssh-gateway-1.0.0
Successfully installed highline-1.5.0
Successfully installed capistrano-2.5.2
6 gems installed
Installing ri documentation for net-ssh-2.0.4...
Installing ri documentation for net-sftp-2.0.1...
Installing ri documentation for net-scp-1.0.1...
Installing ri documentation for net-ssh-gateway-1.0.0...
Installing ri documentation for highline-1.5.0...
Installing ri documentation for capistrano-2.5.2...
Installing RDoc documentation for net-ssh-2.0.4...
Installing RDoc documentation for net-sftp-2.0.1...
Installing RDoc documentation for net-scp-1.0.1...
Installing RDoc documentation for net-ssh-gateway-1.0.0...
Installing RDoc documentation for highline-1.5.0...
Installing RDoc documentation for capistrano-2.5.2...

E agora?

Lá vem a parte 2...

[1] http://www.capify.org
[2] http://labase.nce.ufrj.br
[3] http://www.ruby-lang.org
[4] Segundo solução postada aqui: http://intertwingly.net/blog/2008/11/23/RubyGems-1-3-1-on-Ubuntu-8-10

Thursday, December 4, 2008

Primeira sessão do Dojo Rio

Ontem tivemos a nossa primeira reunião do Dojo Rio [1], no CEFET [2].

Depois de várias semanas mobilizando pessoas, tentando arrumar um lugar adequado para o encontro, e muito papo via lista de discussão, finalmente conseguimos tirar o projeto do papel!

Apesar do horário marcado ter sido 18:30, ficamos aguardando até 19:00 antes de começar -- um atraso de certa forma previsto para um primeiro dia, já que as pessoas não se conheciam direito.
Colocamos um pequeno cartaz (uma folha A4 :P) indicando onde estávamos e avisamos os seguranças da entrada do CEFET para que direcionassem os dojeiros para o lugar certo. Espero que ninguém tenha se perdido por falta de informação.
Para ficar registrado, estávamos no Pavilhão 1 (Informática), na sala 1. Para chegar lá, basta, após entrar no CEFET, seguir reto até o fim do corredor para chegar no Pavillhão 1, que fica à esquerda, e subir a escada metálica logo na entrada do pavilhão.

Antes de começar usamos alguns minutos para nos conhecermos um pouquinho e então, às 19:00, começamos com uma introdução ao Coding Dojo, proferida por mim. Os slides estão aqui.
Os presentes:
  • Edino Moniz
  • Lucas Teixeira
  • Raphael Almeida
  • Rodolfo Carvalho
  • Valdir Monteiro
Finda a introdução, apresentei três problemas sugeridos para a sessão. Discutimos e resolvemos atacar o problema do Amigo Oculto, usando Python. A escolha da linguagem ficou por conta da simpatia que todos demonstraram em relação a ela e a minha familiaridade para poder ajudar o pessoal com os testes :D

Tivemos em torno de uma hora e quinze minutos para programar, fazendo turnos de sete minutos com cada par. Ao fim de um turno o co-piloto assumia o teclado, o piloto voltava para a platéia e alguém da platéia completava o par.

Nós usamos um pequeno script, o pydojo.py [3], para ajudar com os turnos. Encontrei esse programa no github do Dojo@SP, feito pelo pessoal da Async [4]. Trata-se de um ícone na bandeja do sistema com um contador. Ao término do turno ele dispara um alerta para trocar o par, e assim que a troca é feita a contagem reinicia. Fiz uma pequena modificação nele para aceitar o tempo de cada turno como parâmetro na linha de comando. Isso funcionou muito bem para a gente, e foi bem melhor que ficar controlando com cronômetro do relógio.

Estava previsto pararmos de programar 20:30, mas esticamos até 20:40, e depois tivemos nossa retrospectiva e discutimos alguns assuntos, como o próximo encontro (a ser confirmado para a próxima semana no mesmo local), e a seleção de linguagens e problemas.
Chegamos a um consenso de que funciona se as pessoas não tiverem acesso ao problema antes da hora, evitando que já cheguemos na sessão "treinados" para resolver. Então, por enquanto posso me comprometer em levar sugestões, e ficou também a idéia de montar um "banco de problemas". Bem, parece que o Dojo@SP já tem algo nesse sentido [5], que pode e deve servir de base para nós!

Voltando a retrospectiva, nossas idéias convergiram para:

:-)
  • Conhecer pessoas
  • Participação de todos
  • Nenhum imprevisto!
  • Python +
  • TDD

:-(
  • Nem todos dominavam a linguagem +
  • Atraso para começar (previsto...) +
  • Poucos presentes +
  • Teclado e mouse de notebook

Agora precisamos criar um blog para o DojoRio, e um repositório para nosso código.
Continuando com o esquema de votação que temos adotado, escolheremos as ferramentas de acordo com a pesquisa [6].

Já estamos listados no codingdojo.org [7], faltando escrever um texto de verdade para nossa página lá. Também estamos no mapa dos Dojos no mundo [8].

Ah, com fome, antes de nos separarmos e irmos para casa, saboreamos o delicioso espetinho de frango próximo a saída do CEFET.

E assim foi nossa noite. Semana que vem tem mais!


[1] http://groups.google.com/group/dojo-rio
[2] CEFET-RJ, Maracanã - mapa http://tinyurl.com/5pnhyk
[3] http://github.com/dojosp/participant-s-projects/tree/master/Python-Tests/pydojo.py
[4] http://www.async.com.br
[5] http://groups.google.com/group/dojo_sp/web/fontes-de-problemas
[6] http://spreadsheets.google.com/viewform?key=p_t4vVH1AGOyPJtorZ9gpJA
[7] http://codingdojo.org/cgi-bin/wiki.pl?CodingDojos
[8] http://maps.google.com/maps/ms?ie=UTF&msa=0&msid=116400871369678060090.000453a8d6ee3a6d3b8fe

A apresentação / introdução ao dojo:

Intro Dojo Rio
View SlideShare presentation or Upload your own. (tags: codingdojo tdd)