segunda-feira, 19 de julho de 2010

Impacto dos computadores sobre o meio ambiente

Pouca gente se preocupa ou pensa sobre isso. Aqui vai um infográfico que ilustra isso.



----------- keepReading

sábado, 17 de julho de 2010

Exemplo prático de normalização de banco de dados

Este post pretende ser muito menos teórico e muito mais prático sobre um tema que todo desenvolvedor já leu e, provavelmente, procurou saber mais. Normalização de bancos de dados.

O por que?

Claro, antes de meter a mão na massa, a motivação do post. Já vi em muitos foruns perguntas cuja resposta pode ser dada por um design apropriado do banco de dados, feitas tanto por quem está começando, quanto por quem já tem algum tempo de estrada.

Você irá precisar de normalizar os dados em duas situações principais: quando um campo do banco será variável como um vetor, ou quando julgar necessário manter informações devidamente separadas.

Exemplo 1. Vendas e nota fiscal.
Considere a seguinte tabela que armazena as vendas de uma papelaria.

tabelaVendas - desnormalizada
idVendaidClienteitens
113 cadernos, 5 lápis, 1 mochila
2310 lápis, 2 borrachas, 300 folhas de sulfite
372 mochilas, 4 borrachas

Como podemos ver, o campo itens armazena uma quantidade variável de informação. Vamos aplicar a normalização aí na primeira forma normal, ou 1NF.

tabelaVendas - quase 1NF
idVendaidClienteitem 1Qtd 1item 2Qtd 2item 3Qtd 3
11caderno3lápis5mochila1
23lápis10borracha2sulfite300
37mochila2borracha4

OK, separamos as colunas para ter um e apenas um tipo de dado, mas ainda assim podemos ver que o design dessa tabela ainda é bem falho. E se um consumidor quiser comprar mais de 4 itens na mesma compra? mesmo que vc crie 200 campos, certamente haverá um ou outro caso onde esse limite será um problema, sem contar nas desvantagens óbvias de tamanho da tabela, espaço ocupado (com informações nulas), e no trabalho para criar queries e relatórios em cima de uma tabela assim. Mas esta ainda não é a versão terminada da 1NF. Podemos fazer um design um pouco melhor para obter isso.

tabelaVendas - 1NF
idVendaidClientelinhaDaVendaItemQtd
111caderno3
112lápis5
113mochila1
231lápis10
232borracha2
233sulfite300
371mochila2
372borracha4

Pronto, chegamos assim à primeira forma normal. Sem valores multiplos dentro de um campos, e sem multiplos campos para a mesma função.

Os mais atentos devem ter percebido um pequeno "problema" na tabela acima. Não há nenhum campo que funcione bem como chave-primária. A chave-primária para se obter uma linha é a combinação das colunas idVenda e linhaDaVenda.

Para resolver isso aplicamos a segunda forma normal, 2NF, que diz que cada linha tem uma chave-primária representada por um e apenas um campo.

Considere a tabela anterior com um pouco mais de detalhes:
tabelaVendas2 - 1NF
idVendaidClienteDatalinhaDaVendaQtdidProdutodescProduto
1112/7/2010131caderno
1112/7/2010252lápis
1112/7/2010313mochila
2313/7/20101102lápis
2313/7/2010224borracha
2313/7/201033005sulfite
3715/7/2010123mochila
3715/7/2010244borracha

Vamos separar o que pertence a cada venda, do que pertence aos items de cada venda em duas tabelas.

tabelaVendas2 - 2NF
idVendaidClienteData
1112/7/2010
1112/7/2010
1112/7/2010
2313/7/2010
2313/7/2010
2313/7/2010
3715/7/2010
3715/7/2010

tabelaDetalheVenda - 2NF
idVendalinhaDaVendaQtdidProdutodescProduto
1131caderno
1252lápis
1313mochila
21102lápis
2224borracha
233005sulfite
3123mochila
3244borracha

Note que as duas tabelas estão agora relacionadas pelo campo idVenda, que é chave-primária da primeira tabela e chave-estrangeira na segunda.

Estamos agora a um passo da terminar a normalização destas entidades (geralmente se aplica a normalização até sua terceira forma normal. Dificilmente formas de normalização superiores se fazem necessárias). Vamos separar os dados dos produtos da tabela de detalhes de venda (Note que a tabela de vendas já está completamente normalizada).

tabelaDetalheVenda - 3NF
idVendalinhaDaVendaQtdidProduto
1131
1252
1313
21102
2224
233005
3123
3244

tabelaProdutos - 3NF
idProdutodescricaoProduto
1caderno
2lápis
3mochila
4borracha
5sulfite

Pronto. Normalizado.
Se você tem interesse vale a pena também dar uma lida em como desnormalizar (de forma ordenada) um banco de dados, que também é conhecido como formas 4N ou superiores.
----------- keepReading

Standards e a praticidade

Antes de prosseguir com o texto, vou deixar uma coisa bem clara: sou um standardista, ou em outras palavras, acredito que seguir normas e padrões pré-definidos de uma tecnologia são o caminho mais correto e eficiente para garantir um bom produto.

Dito isto, explico o por que. Eu estou participando das listas de discussão do HTML 5 da WHATWG e da W3C. Para cada ponto a ser incluído na norma, existe muita discussão por gente que faz web. Será que vai ser útil mesmo? Existe algum conflito ou incompatibilidade grave sobre a proposição? Alguma brecha de segurança ou de interpretação? Tudo isso é discutido. Muito discutido. Assim, não tem como falar que padrões são besteira ou inúteis. Eles estão aí para ajudar.

Certo, estamos com o HTML 5 batendo à porta. Não só o HTML propriamente dito, mas todo um conjunto de novas tecnologias, tanto que HTML 5 já virou praticamente uma nova buzzword, um novo jargão, como Web 2.0, AJAX e Cloud Computing.

Temos novas tags, novos atributos, reformulação de muitas tags e atributos, integração com microformats... tudo isso só no HTML 5. Fora o CSS 3, as novas APIs de Javascript e do DOM (Document Object Model).

Enfim, tudo isto está aí, emergindo. Alguns desenvolvedores já estão de olho. Livros, video aulas, blogs... No mercado de navegadores, uma competição para saber quem vai suportar o que, quando e de que forma. E os buscadores também pressionando para poderem agregar essas novas tecnologias (que nunca e jamais substituirão o bom conteúdo!).

A hora de cair no HTML5 e outras novas tecnologias é agora?

Sim e não. Sim porque é a tendência, e quanto mais rápido nós desenvolvedores aprendermos, melhor. E não por uma questão técnica: suporte dos navegadores.

Não creio que valha a pena investir demais agora em recursos que são suportados por apenas um ou outro navegador cujas participações no mercado de browsers é mínima. As tão faladas tags <video> e <audio>, transformações em CSS, e WebSockets ainda não são nada viáveis ou muito mal suportadas.

Assim, do meu ponto de vista, faz sentido deixar alguns recursos de lado por enquanto, pelo menos os que são mal suportados pelos principais navegadores, ou que exigem alguma espécie de gambiarra para funcionar direito, até que o suporte entre os principais navegadores seja bom.

Até mais.

----------- keepReading

Testes de requisito

Para começar a explicar em detalhes os principais tipos de testes existentes vou atacar a base: testes de requisitos.

Por que testes de requisitos?

Em uma frase: porque é onde os projetos nascem.

Qualquer software, desde uma simples calculadora até uma aplicação de grande porte passa de alguma forma por esta fase, mesmo que informalmente.

Definir requisitos é descrever um pedido, uma idéia, aquilo que nós (ou geralmente o nosso cliente) quer que o programa faça.

Fica aqui a primeira nota importante para qualquer projeto, de qualquer tamanho, e que tem valor não apenas do ponto de vista técnico, quanto do ponto de vista administrativo e financeiro: escreva os requisitos.

Essa parte, assim como quase sempre a documentação é inexistente em projetos pequenos. E não é por menos que é a maior fonte de dor de cabeça para pequenos empresários, programadores, testers e pro próprio cliente.

Creio que todo leitor aqui já deve ter passado pela situação onde o cliente pede algo, você cria, e depois de terminado, o cliente diz que queria que algumas funcionalidades fossem diferentes, e quase sempre são funcionalidades que mexem com a base do programa, do modelo adotado para a solução. Coisa boba que se explicada de início, pouparia muito tempo, retrabalho e desgaste por todas as partes mais tarde.

OK, requisitos são importantes. Mas como testá-los?
Em pequenos e médios projetos, geralmente a documentação sobre especificações técnicas é inexistente, ou feita apenas através de emails. O primeiro passo é escrever os requisitos. Não é necessário muito, mas tem que ser algo que dê bases aos outros times (design, implementação e teste).

Exemplo. Um cliente pede uma loja virtual, onde ele vai vender vários produtos e, na interface, os consumidores poderão procurar/navegar pelas diversas categorias. Ele quer poder incluir e excluir produtos na lista, tem um sistema de venda, com acompanhamento de pedidos, e um sistema de relatórios sobre as vendas e sobre o estoque.

Bastante trabalho pela frente, mas poucas linhas para descrever. Faltou alguma coisa? Muitas. Mas para este exemplo, vou pegar um ponto que certamente poderia ser um foco de divergência: as categorias.

Perguntas básicas a se fazer sobre o "simples" sistema de categorias.
  1. O cliente irá usar apenas categorias pré-definidas, ou quer poder gerenciar as categorias (criar novas, remover existentes...)?
  2. Essas categorias poderão ter subcategorias?
  3. Um produto pode pertencer a mais de uma categoria ou subcategoria?

Aí estão perguntas cujas respostas podem significar desde uma nova tabela e formulários no banco de dados, até uma reestruturação de boa parte do banco de dados e da aplicação.

Outro exemplo. O cliente pede um sistema onde um usuário poderá se cadastrar.
Simples, não? Não. Você não pode supor que se trata de uma newsletter, nem supor que se trata de um formulário altamente complexo. O certo é esclarecer.

  1. Cadastrar o que? email apenas? ou endereço, telefone...?
  2. Quais são os campos obrigatórios e quais são opcionais?
  3. Clientes de onde? de um estado específico? de uma cidade específica? um continente? do mundo?

Faça uma lista de perguntas. Se possível, ajude seu cliente sugerindo prováveis campos e indicando quais você acha que seriam os obrigatórios, mas deixe que o cliente decida e diga o que é que ele realmente quer, afinal o negócio é dele.

Resumindo

Sempre que você chegar numa situação onde para testar ou criar você tenha que supor ou adivinhar o que o cliente quer, pergunte, esclareça. Vale a pena o investimento, porque é necessário pouco tempo para obter respostas que podem significar uma diferença enorme em termos de prazo e preço a todos os lados envolvidos.

Abraços e até a próxima.
----------- keepReading

segunda-feira, 12 de julho de 2010

Testes de Software - Tipos de teste

Falar em testes de software pode ser um tanto subjetivo. Para evitar essa abertura de interpretação, até mesmo porque é um serviço que exige rigor, vamos ver os tipos de teste. Quais são, quais as finalidades e como avaliá-los no processo.

Existem vários tipos de teste, cada um para avaliar uma determinada característica da aplicação.

Testes funcionais - estes são os testes que servem para determinar se a aplicação faz o que se propõe a fazer. Comecei falando deste tipo, porque certamente é o primeiro teste que se passa na cabeça dos programadores.

Como exemplo simples para teste de um sistema de login:
UserPassResultado esperado
válidoválidoLogin bem sucedido
inválidoválidoMensagem de erro
válidoinválidoMensagem de erro

Este é o exemplo mais básico, só para ilustrar. Como veremos adiante, existem outros cenários e casos de uso que aumentam a complexidade da bateria de testes.

Testes de performance - nem sempre este tipo de teste se faz necessário. Eles avaliam o tempo de resposta, ou quantidade de memória usada, ou algum outro parâmetro crítico em função da variação da quantidade de inputs ou requisições. São os famosos benchmarkings e variantes. Veremos mais detalhes num post futuro.

Testes de segurança - Há muito o que se discutir aqui. O que é hacking, o que não é, quais as técnicas, quais as medidas de proteção, como considerar algo como seguro... Responder essas questões básicas que servem de base para os testes é que são o maior desafio, e motivo de discussão.

Teste unitário - quando se procura alguma ferramenta ou alguma informação sobre testes em alguma linguagem específica, isto é geralmente o que se encontra. Testes unitários são testes realizados para cobrir um módulo isolado, uma função, ou um conjunto de funções intimamente relacionadas. São similares aos testes funcionais, só que aqueles podem ser extensos, enquanto estes são direcionados e geralmente curtos. Por serem testes simples, rápidos, e com foco na parte e não no todo, eles são muito importantes, e boa parte dos erros são encontrados através deles.

Teste de requisito - pode soar estranho para quem nunca trabalhou com QA antes, mas aqui está o teste mais fundamental de todos, seja qual for o modelo de desenvolvimento ou aplicação desenvolvida. Testar os requisitos é EXTREMAMENTE IMPORTANTE porque:
1. Evita interpretações dúbias tanto da parte dos desenvolvedores, quanto de quem testa;
2. Decorrente da afirmação acima, facilita o trabalho (e diminui o retrabalho) dos arquitetos, designers e implementadores;
3. Apesar de aumentar o tempo necessário inicial dos trabalhos, o ciclo completo de desenvolvimento é reduzido, reduzindo custos do projeto;
4. Evita a criação de testes desnecessários, ou com expectativa falsa, não só reduzindo o trabalho de testes como também aumentando a produtividade (erros encontrados x numero de testes) em quality assurance.

Revisão de código - tipo de teste feito pelos desenvolvedores. É precedente ao Unit Test, e se faz com a inspeção criteriosa do código. Vale a ressalva que nas metodologias ágeis, onde se programa em dupla, esse tipo de teste é mais comumente feita. Um faz um trecho do código, e o outro revisa.

Teste de cobertura de código - Primo do Unit Test, este tipo de teste visa garantir que todos os possíveis caminhos dentro do código sejam acessados. A principal diferença entre este e o Unit Test, é que este é mais abrangente.

Teste de atomicidade - pouco conhecido, este tipo de teste visa garantir a tipagem de dados. Uma variante deste tipo de teste mais difundida é a Fault Injection Test, onde se introduzem inputs não esperados, geralmente usando caracteres de controle mesclados os inputs normais, e observando o comportamento do sistema.

Teste de integração - testa a integração entre módulos e componentes para checar sua compatibilidade e eventual quebra no fluxo do processo.

Teste de recuperação de falha - tipo de teste feito geralmente em aplicações que funcionam como servidores (servidores HTTP, servidores de bancos de dados), sistemas operacionais e sistemas de arquivos. Neste tipo de teste, o sistema é forçado a falhar de alguma forma, e depois se verifica como ele se recupera de um erro grave, se há perdas de ddos e afins.

Testes de regressão - em softwares que são desenvolvidos em ciclos, esta é uma bateria que é feita ao finaldo ciclo funcional, e onde as funcionalidades anteriores que não foram modificadas são postas à prova, afim de garantir que nenhuma funcionalidade antiga foi alterada.

Teste de instalação - para software que serão distribuídos, este é o teste onde se verifica se a instalação será bem sucedida nas plataformas-alvo.

Testes estáticos - são os testes onde se observam o estado do sistema. Inspeções de códigos, e estabilidade inicial pertencem à esta classe de testes.

Testes dinâmicos - são feitos colocando o sistema para executar, com entradas, e então parando a execução e observando o estado das variáveis. Quem usa o Firebug para depurar javascript, ou o gdb para depurar um código em C++ sabe do que se trata.

Testes manuais - são feitos um a um, com um usuário criando dados e fazendo sua inserção no sistema.

Testes automatizados - é quando se usa algum tipo de ferramenta para auxiliar na execução de testes, agilizando o processo. Testes de performance, segurança, carga, estabilidade e regressão são alguns tipos de teste onde vale a pena usar tecnologias que automatizem o processo de teste. Vale notar que o que garante a qualidade deste tipo de teste (onde milhares, às vezes milhões de testes são rodados de uma única vez) é a massa de dados utilizada no input. Se ela for inválida ou inconsistente, a bateria de nada vale.

Fora do cunho técnico e relacionado com a criação de códigos propriamente ditos, ainda temos outros tipos de teste que podem ser feitos.

Testes de acessibilidade - verifica se o programa é acessível sem o uso de mouse, de tecnologias opcionais (addons), avalia a disposição dos elementos, contraste de cores, tamanho e legibilidade de fontes...

Testes de usabilidade - Verifica se o software é de fácil utilização. É um tipo de teste que quando bem feito e bem trabalhado, pode significar a diferença entre o sucesso e o fracasso de uma aplicação. Card sorting, eye tracking, mouse tracking e simple task são alguns dos tipos de teste de usabilidade.

Testes de internacionalização - verifica se a aplicação se comporta de forma adequada em termos de linguagens e seus padrões (padrão para o formato da data, da hora, da moeda, separador decimal, direção do texto).

Teste de documentação - no caso de produtos voltados à indústria, geralmente se faz o teste da documentação afim de avaliar a qualidade em termos de utilidade da mesma (o usuário consegue as instruções necessárias para efetuar alguma tarefa na documentação?). Alguns sites fazem o teste de suas FAQs.

Se você nunca leu nada antes a respeito sobre Quality Assurance deve ter se assustado com a quantidade de tópicos que temos dentro da área. Nos posts seguintes, vou procurar explicar o que é e como se faz cada tipo de teste, mas lembre-se que assim como segurança, qualidade é algo subjetivo, e devem ser considerados quais são os aspectos mais importantes para dimensionar corretamente o tempo e a verba empregadas nisso.

Se você vai fazer um site com estimativa de visitação máxima de 15 usuários logados simultaneamente, provavelmente não precisa de um ciclo de performance, ao passo que se você está desenvolvendo um software de CAD/CAE/CAM ou modelagem 3D com ray tracer, análises de performance serão tão importantes quanto testes funcionais.

Bom pessoal, é isso. Até a próxima.
----------- keepReading

Qualidade de Software

Na China antiga havia uma família de curandeiros, um dos quais era conhecido em toda a terra e trabalhava como médico de um grande senhor. O médico foi perguntado sobre quem de sua família era o curandeiro mais habilidoso. Ele respondeu:
"Eu atendo os doentes terminais com tratamentos dramáticos, e de vez em quando alguém é curado e meu nome sai entre os senhores."
"Meu irmão mais velho que eu cura a doença quando ela começa a criar raízes, e suas habilidades são conhecidos entre os camponeses locais e vizinhos."
"Meu irmão mais velho é capaz de sentir o espírito de doença e erradicá-la antes que ele tome forma. Seu nome é desconhecido fora de nossa casa."

Com esta parábola chinesa, abro este artigo para falar sobre qualidade de software e sua importância.

No últimos 2 anos e 3 meses, minha principal função foi trabalhar com Software Quality Assurance (Controle de Qualidade Software). Uma área vasta e que eu praticamente desconhecia antes. Obviamente aprendi muita coisa, e talvez a principal delas tenha sido ser um desenvolvedor melhor, mais criterioso.

Antes de trabalhar nessa área, eu era basicamente um desenvolvedor web, com o básico das apostilas da que se encontram facilmente na internet, e uma ou outra experiência agregada através dos projetos no tocante javascript (fiz algumas coisas interessantes e aprendi na marra a lidar com o DOM).

Sou inegavelmente um perfeccionista e autodidata. Sempre fui ligado nos aspectos de segurança, performance, standards compliance, acessibilidade, SEO, entre tantos outros, ainda que de forma amadora, sendo procurando artigos, benchmarkings, novas técnicas e tentando aprender a forma certa - ou a melhor forma entre as certas - de se fazer as coisas.

Admito que eu tinha uma visão muito estreita do que é Quality Assurance, afinal eu já programava, raramente alguma coisa que era feita por mim apresentava algum bug depois de entregue (claro que rolava aquele teste básico pra ver se tudo estava em ordem antes), e as coisas que eu fazia funcionavam. Não entendia a necessidade de testar algo que já era funcional.

Eu achava que ser um tester era algo como um passo para trás na carreira. Ledo engano. Ser um bom tester, exige que você seja antes de tudo, um bom programador (ou tenha uma idéia realmente boa de como as coisas funcionam por baixo dos panos), afinal, você vai submeter o trabalho de um programador à uma bateria de testes diversos, afim de encontrar falhas.

Para ilustrar, não raro uma coisa me acontecia - e creio que todo programador passou ou passa por isso vez ou outra: você cria o programa, faz alguns testes básicos, vê que funciona, olha novamente e código e se sente orgulhoso do trabalho que fez e entrega. Dias depois - ou horas depois - é comunicado de que encontraram uma falha, geralmente é o cliente que encontra, e é algo simples, mas que não foi testado. Você abre o código, e depois de alguns minutos - às vezes horas - procurando, acaba encontrando um erro bobo num if da vida. Você havia lido e relido várias vezes o if, e nem imaginava que o erro estaria ali, porque numa leitura à primeira vista, tudo parecia em perfeita ordem.

Bom, pretendo dar início neste post a uma série de tutoriais sobre metodologias e técnicas sobre QA. Mas nada técnico por enquanto. Este post é apenas uma introdução.

Mas a primeira e mais importante dica é formalizar. Escrever e tomar notas sobre o que foi feito e o que não foi feito, sobre o que funciona, e o que não funciona.

Seja num arquivo txt, ou numa planilha, anotar o que foi testado evita que o programador pense que funciona para todos os casos e seja depois surpreendido por um erro que ele achava que funcionava.
----------- keepReading

domingo, 11 de julho de 2010

O grande desafio da web

É inegável que a internet revolucionou a que nós vivemos. Compartilhar fotos, vídeos, músicas, conversar com amigos e parentes distantes em tempo real, fazer novas amizades - e, por que não, amores (sou um exemplo vivo disso) - dividir conhecimento, agilizar processos, integrar negócios... Tudo isto é possível por causa da internet.

Ainda que haja muito lixo virtual, o potencial criativo e benéfico da internet como plataforma e meio de comunicação é expressivo, comparável talvez somente aos adventos da prensa de Johannes Gutenberg e à radiodifusão.

Mas ao contrário de suas revoluções predecessoras, a internet é muito mais complexa. Tanto por ser mais flexível e apresentar uma grande diversidade de tecnologias, quanto por exigir que estas tencologias respeitem padrões que interessam a diversos ambitos. Em outras palavras, criar algo realmente com qualidade, aceitável em todos os aspectos é uma tarefa para titã.

Digo isso porque até hoje não vi nenhum, absolutamente NENHUM framework que respeite isso. Se é fácil de mapear banco de dados para a aplicação, pode ser que seja falho na segurança. Se implementa recursos modernos, como tecnologias assíncronas (Ajax, E4X) ou RIA [Rich Internet Application, onde tem no flash dinâmico seu ilustre exemplo], é quase certo que vai pecar na acessibilidade. Se é bonito ou rápido de desenvolver, provavelmente vai mandar por água abaixo padrões para findability, essenciais ao SEO (Otimização para buscadores).

Talvez por isso até hoje eu não tenha terminado meu próprio framework. Devido ao meu lado perfeccionista. Não me arrependo, mas estou muito motivado a criar algo de qualidade.

Só para ilustrar, eu gostaria que meu framework pudesse:
1. Se conectar ao BD, mapear os metadados e relações, e com base nisso criar os objetos e formulários.
2. Ao efetuar operações, manter a segurança, tanto evitando os tipos básicos de ataque, quanto verificando se os dados respeitam o modelo definido no banco.
3. Permitir que regras de negócio fiquem numa camada separada, facilmente editável.
4. A mesma flexibilidade para as views
5. Que trabalhe nessa construção de operações e estruturas da forma Progressive Enhancement, ou seja, funcional mesmo se não houver javascript ou flash disponíveis.
6. Que tenha em vista o SEO como uma medida - quiçá como uma característica - promovendo URL realmente amigáveis, e não os padrões talhados e engessados, muitas vezes que só disfarçam o verdadeiro sentido da composição das URLs para programadores que gostam de padrões convenientes.
7. Que seja aberto a novas tecnologias (HTML 5, CSS 3), e use as existentes de forma eficiente e inteligente (recursos dentro dos bancos de dados, XSLT...)

Agora, se você é um empresário, ou tenha interesse em manter um site, fica aqui a justificativa de porque alguns profissionais cobrarem muito mais do que outros. A explicação é essa: a importância que verdadeiros profissionais dão aos produtos e serviços que vendem e prestam.

Fazer algo com qualidade passa longe de ser simples. O resultado é bem diferente, e faz diferença se seu negócio vai pra frente ou não no mundo virtual. E é claro, qualidade tem preço.

Imagine um profissional que dê conta de fazer um site com todas as características acima, e ainda por cima um designer que empregue identidade visual, acessibilidade através de contraste adequado, composição, semantica no código (que ajuda no SEO), tipografia e convenções que façam a diferença para quem lê e usa o site...

Não, não é fácil mesmo fazer um simples site. Há muito mais trabalho e técnica do que o sobrinho que aceita fazer por $300 sonha em existir.

Bom, é isso, fica aqui o registro, meio desabafo, meio mirando no futuro.

Keep reading, fellas.
---------- keepReading