segunda-feira, 1 de novembro de 2010

Testes de Software - Inspeção de código e teste unitário

Fala pessoal, olha eu reacendendo a série sobre testes de software. No último post falei sobre testes de requisito. Agora é hora de falar do segundo lugar onde mais se encontram e corrigem defeitos: inspeção de código e testes unitários.

A inspeção de código

Este teste se caracteriza pelo fato de ser executado apenas por quem entende o código-fonte, quem vai testar precisa ser desenvolvedor também. Aqui cabem várias abordagens. Uma delas é quem fez o código é quem faz a inspeção, mas ao mesmo tempo que quem escreveu o código é a pessoa que mais o compreende, seus olhos também correm o risco de estar viciados naquilo, então uma outra abordagem é um desenvolvedor que não fez o código que o inspecione. Uma terceira é revisar em grupo. Acho esta última a mais segura.

Note que a inspeção de código é uma revisão, um highlevel checkout, você lê e tenta achar erros óbvios de lógica ou gramática/digitação. Não mais que isso. Existem outros métodos mais complexos e formais de verificação. A inspeção é importante porque é rápida em detectar erros grosseiros (ou nem tanto).

Testes unitários

Testes unitários são o teste de comportamento de uma função, de um método ou de um conjunto intimamente relacionado de funções ou métodos. Eles são a base dos testes dinâmicos (aqueles realizados executando de fato o programa), e por serem altamente específicos, aumentam a confiabilidade dos testes.

Eles geralmente são automatizados, ou seja, você escreve um programa para executá-los.

Vou me estender no assunto, porque a automação de testes é algo válido para muitos outros tipos de teste (funcionais, cobertura de código, segurança, performance...).

Vamos supor que você tenha um código como:
<php
function soma($a, $b) {
return ($a + $b);
}
?>


VocÊ pode testar esse código com uma função similar a:
<php
function testSoma() {
$source = array(
array(1,1,2),
array(0,1,1),
array(1,0,1),
array(0,-1,-1),
array(-1,1,0),
array('non', 'sense', "??")
);

$i = 1;
foreach ($source as $unit) {
if (soma($unit[0], $unit[1]) === $unit[2]) {
echo "Test $i OK
\n";
} else {
echo "Test $i FAIL. Dataset: " . print_r($unit, TRUE) . "
\n";
}
$i++;
}
}

testSoma();
?>


Não se deixe enganar pela simplicidade dos exemplos. Automatizar testes traz uma série de vantagens. Uma delas delas é livrar o código principal de echos e prints. Mantendo a integridade do código e separando os conceitos. Sempre que você sentir a necessidade de checar de alguma forma qual o estado atual do programa, escreva um teste.

Uma outra vantagem é a possibilidade de reuso. Se você alterar algo no código, poderá executar a mesma bateria de testes, sem precisar reescrever.

E uma última vantagem com o exemplo que dei, é um conceito bastante poderoso e utilizado em automação de testes: os datasources. Eles permitem que você mude o conjunto de inputs, sem alterar a função verificadora.

No exemplo utilizei um array, escrito à mão mesmo. Porém, em ambientes de produção, você pode criar seus datasources em planilhas ou bancos de dados, o que facilita tanto a criação como a manutenção.

Existem vários frameworks disponíveis para realizar testes unitários. Aqui está um ótimo tutorial sobre o uso de frameworks de teste unitário.

OK, agora vamos dissecar um pouco mais o exemplo que dei acima. Temos uma função a ser testada, uma função para testar que contém um datasource e um loop que verifica e imprime o resultado dos testes. Para ambientes de produção ou cujo volume de testes se torne alto o suficiente, podemos e devemos refatorar deixando as responsabilidades divididas. O esquema muito utilizado é o seguinte:

-> uma função para obter o datasource
-> uma função de logging, para registrar os resultados
-> uma função que faça o papel de juntar tudo (chama a função, itera sobre os dados do data source e registra o resultado).

Com essa estrutura fica fácil manter os inputs e controlar os resultados.

Voltando ao Unit Test. O Unit Test é a base dos testes dinâmicos. A maioria dos bugs é encontrada aqui, porém vale ressaltar que mesmo softwares que passam pelo unit test podem conter bugs, isto porque o unit é focado na parte e não no todo. Assim, nos testes funcionais e de integração podem aparecer bugs novos que não poderiam ter sido detectados antes. Esta questão passa muito mais por design e por arquitetura do que propriamente a funcionalidade específica de um módulo, método ou classe.

Agora um exemplo mais real e prático de Unit Test. Vamos supor que você tenha um processo de login no seu site. Um usuário fornece nome e senha, e você tem uma função que verifica a validade e loga o usuário. Para efeitos de simplicidade:

<php
/*
retorna TRUE se a combinação é válida
retorna FALSE se a combinação não é válida
*/
function isValidUser($username, $pass) {
// código que checa as condições
}
?>


O seu datasource (viu, se você tiver as ferramentas/métodos certos para reutilizar, 95% do seu trabalho vai ser focar apenas nos data sources) será mais ou menos assim:

usernamepassexpected
user1 pass1 TRUE
user1 '' FALSE
user1 inválido FALSE
'' pass1 FALSE
inválidopass1 FALSE
user1 pass2 FALSE
'' '' FALSE
inválidoinválido FALSE

Legenda:
user1 = username 1
pass1 = senha correspondente ao user1
'' = vazio
inválido = username/password não existente nos registros
pass2 = senha correspondente ao user2

Bem, espero que o conceito e a importância dos unit tests e da automação tenham ficado claros neste post. Até a próxima.
----------- keepReading

quinta-feira, 28 de outubro de 2010

Exemplo de desnormalização de banco de dados

Se você já trabalhou a mais de 2 meses em TI já ouviu os termos "normalização de banco de dados" e
"boas práticas". E provavelmente também ouviu a relação das duas coisas: é uma boa prática normalizar um banco de dados.

Não há o que se discutir sobre isso numa primeira instância. Sim, porque uma das técnicas avançadas de tuning é a desnormalização. Aqui vamos a um exemplo prático de como se fazer isso. Mas antes, as ressalvas:

- Aprenda a normalizar um banco de dados de forma decente, nada de fazer um serviço porco e querer dizer que é um padrão avançado inventado no Paquistão.

- Assim como para normalizar, estude o que pode ser desnormalizado. O processo é um método, racional e com suas próprias regras.

O exemplo.

Vamos supor que você cuide de um portal de artigos médicos, que são publicados sempre em 4 línguas (inglês, francês, alemão e espanhol).

Numa forma normalizada, você teria um banco de dados com estrutura semelhante a:

Tabela [autor]
- id
- nome

Tabela [idioma]
- id
- idioma

Tabela [artigo]
- id
- autor_id

Tabela [artigo_contents]
- id
- artigo_id
- idioma_id
- conteudo

Como vemos acima, temos uma tabela para idioma, uma para artigo com informações gerais, e uma tabela com o conteúdo dos artigos, indexada ao artigo e ao idioma. Essa estrutura facilita a organização dos conceitos uma vez que está normalizada.

Agora como saber se essa estrutura poderia ser otimizada e desnormalizada? Bem, poderíamos ter o conteúdo na própria tabela artigos. Antes de prosseguir com a idéia, precisamos responder a algumas perguntas:

- o número de idiomas vai/pode mudar? quais as chances disso acontecer?
- pode ser que um artigo seja publicado num número parcial de línguas, por exemplo só em inglês e alemão?


A resposta a estas perguntas é que determina se a atual estrutura do banco de dados pode ser desnormalizada ou não.

Se o número de línguas NÃO vai mudar ao longo do tempo, ou a chance disto é extremamente remota E os artigos serão todos sempre publicados em todas as línguas, então podemos desnormalizar o banco de dados, movendo o conteúdo para a tabela artigos.

A nova estrutura seria semelhante a:

Tabela [autor]
- id
- nome

Tabela [artigo]
- id
- autor_id
- conteudo_en
- conteudo_de
- conteudo_fr
- conteudo_es

Muito mais simples. A vantagem explícita desse tipo de operação é a simplicidade na hora de escrever e rodar as queries.

Antes deveria ser algo como:

SELECT c.conteudo, at.nome FROM artigo_conteudo c, autor a WHERE a.id = artigo.autor_id AND ac.artigo_id = artigo.id AND ac.idioma_id = '2' AND artigo.id = 27;

Agora se traduz em algo como:

SELECT a.conteudo_fr, at.nome FROM artigo a, autor at WHERE at.id = a.autor_id AND artigo.id = 27;


Reduzindo o número de joins e tudo o mais. Claro, vai ser necessário refatorar parte dos códigos, mas pode valer a pena. O ideal é desnormalizar ANTES de criar o código, na fase de design da solução. Assim o custo de refactor é 0.
----------- keepReading

sábado, 9 de outubro de 2010

Turbinando suas buscas internas

O problema

Quase sempre vemos um formulário de busca interna em sites que publicam conteúdos com frequencia. Este formulário, assim como a navegação por categorias ou por tags, ajuda muito os visitantes a encontrarem o que procuram. Mas desenvolvedores iniciantes - ou nem tão iniciantes assim - ainda implementam esse mecanismo de busca de uma forma "crua".

Geralmente é um simples formulário, com método GET, um campo de texto e um botão de OK. Até aí, sem problemas. No front end é isso mesmo. Mas no back end, geralmente a implementação que vemos é algo assim:


<?php
$terms = explode(' ', $_GET);
$query = "SELECT * FROM table WHERE conteudo LIKE ";

foreach ($terms as $term) {
$query .= "%" . $term . "% OR LIKE";
}
... // roda a query, pega resultados, mostra
?>


Pois bem, esta abordagem, apesar de funcional, não é elegante, nem tem a precisão desejável. Se você buscar por "um cone" pode encontrar "a coleção de ícones do Humberto". Isso sem falar da performance das queries, que é seriamente afetada quanto mais registros no banco e mais termos são procurados.

A solução? Um recurso que os principais RDBMS tem chamado Full-Text Search. Vou ensinar aqui como implementar e usar estes recursos na prática (no MySQL e no PostgreSQL).

MySQL

NOTA: o recurso de Full-Text Search só está disponível para tabelas MyISAM.
Vamos supor que você tenha uma tabela de posts ou artigos com a estrutura parecida com a seguinte:

id
titulo
intro
conteudo
autor
tags
publishdate

Em primeiro lugar vamos precisar adicionar um índice, que é composto pelos campos que desejamos que sejam buscados. Rode isso pelo seu programa de administração do MySQL (MySQL Admin, phpMyAdmin).

ALTER TABLE posts ADD FULLTEXT (titulo, intro, conteudo, tags);


Depois disso, basta reescrever a query no PHP:

SELECT * FROM posts
WHERE MATCH (titulo, intro, conteudo, tags)
AGAINST (". $_GET .");


A busca será mais rápida e os resultados com menos falsos positivos. Sem contar que o código fica mais limpo e elegante.

PostgreSQL

O PostgreSQL dá um pouco mais de trabalho para fazer a implementação.

Pelo seu programa de administração, rode as seguintes queries.

ALTER TABLE posts ADD COLUMN postsfts tsvector;

UPDATE posts SET postsfts =
setweight(to_tsvector('pg_catalog.portuguese', coalesce(titulo,'')), 'A') ||
setweight(to_tsvector('pg_catalog.portuguese', coalesce(intro,'')), 'B') ||
setweight(to_tsvector('pg_catalog.portuguese', coalesce(conteudo,'')), 'C');

CREATE INDEX postsfts_idx
ON posts
USING gin(postsfts);

CREATE TRIGGER postsfts_tg
BEFORE INSERT OR UPDATE ON posts
FOR EACH ROW EXECUTE PROCEDURE
tsvector_update_trigger('postsfts', 'pg_catalog.portuguese', 'titlulo', 'intro', 'conteudo');


Entendendo o que foi feito.
A primeira query adiciona um outro campo à tabela, onde o vetor de busca ficará armazenado (isso aumenta de forma significativa a performance).
A segunda query define quais campos farão parte do full-text search. Note que no PostgreSQL você pode definir o peso de cada campo - A, B, C ou D, sendo A o campo mais importante, D o menos importante. Ah, e claro definimos a linguagem para português.
Na terceira query, construímos um index sobre o campo que acabamos de criar.
Por último, criamos um trigger que vai atualizar o campo que criamos, sempre que o o conteúdo da tabela for atualizado.

Com isto feito, podemos transformar as queries no PHP para:

SELECT * FROM posts, to_tsquery(' . $_GET . ') query
WHERE query @@ postsfts;


No PostgreSQL o operador "@@" funciona como o MATCH AGAINST do MySQL. E precisamos converter o que vai ser buscado através da função to_tsquery().

Qual é o mais relevante?

Os recursos de full-text search permitem ir um pouco mais além e definir um ranking sobre quais registros são mais relevantes sobre os termos buscados. No PostgreSQL isso pode ser feito com uma query similar à seguinte:


SELECT *, ts_rank_cd(postsfts, query) AS rank
FROM posts, to_tsquery(' . $_GET . ') query
WHERE query @@ postsfts
ORDER BY rank DESC LIMIT 5;


Aqui temos a função ts_rank_cd que faz essa função do ranking (além de já termos setado os pesos dos campos antes). E vocês podem notar que estamos ordenando pelo ranking. Ou seja, essa query trás os 5 resultados mais relevantes.

Para obter o mesmo efeito no MySQL podemos fazer a query da seguinte forma:

SELECT *, MATCH (titulo, intro, conteudo, tags) AGAINST (". $_GET .") AS rank
FROM posts
WHERE MATCH (titulo, intro, conteudo, tags) AGAINST (". $_GET .")
ORDER BY rank DESC LIMIT 5;


Conclusão

Espero ter criado um guia rápido e prático para que vocês possam aplicar o full-text search em seus próximos trabalhos e assim obter melhores resultados. Vocês codificam mais rápido, o sistema ganha em performance, e os usuários agradecem os resultados mais precisos.

Abraços e keep readin'

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

segunda-feira, 20 de setembro de 2010

PHP Mail Class

Subi hoje no sourceforge uma classe que fiz a quase um ano, e que me ajudou muito.

É uma classe para envio de emails no PHP, inclusive com arquivos anexados. A classe é bem simples, enxuta e muito útil (senão eu não estaria compartilhando).

Dúvidas? Deixe um comentário.
----------- keepReading

sexta-feira, 17 de setembro de 2010

Tipografia básica para web

Desde o meu início com o desenvolvimento para web, sempre foquei mais em programação, códigos, bancos de dados do que em design. Isto por aptidão. E se falta talento e bom gosto, também me faltou informação simples, direta e suficiente para criar algo esteticamente atraente. Não uma obra-prima, mas visualmente decente.

Vamos ao básico da tipografia, que é a área do design que estuda as fontes, as letras e suas formas. A intenção não é gabaritar o leitor a ser um especialista no assunto, longe disso, e sim fornecer as bases para que as produções venham a ter funcionalidade e cumprir decentemente com seu propósito.

Tipos de fonte

Com serifa e sem serifa (serif e sans-serif).


As serifas são os pequenos traços ou prolongamentos no fim das hastes das letras. A divisão das fontes entre tipos serifados e sem serifa é a principal divisão na tipografia.

Na mídia impressa, costuma-se usar fontes serifadas para o texto, e fontes sem serifa para títulos e outros elementos de destaque, isso mais por uma questão funcional do que estética. As serifas dão uma impressão de continuidade, o que facilita a leitura, e por isso é usada no fluxo de texto. Já as fontes sem serifa não dão esta sensação de continuidade, funcionando bem para elementos de destaque.

Mas na web a história muda. Fontes serifadas são mais difíceis de ler em monitores, e o uso de fontes sem serifa facilita a leitura.

Além da diferenciação entre fontes com e sem serifa, existem outras propriedades importantes de fontes. Os espaçamentos são uma dessas características. As fontes geralmente têm largura diferentes para letras diferentes. Quando a largura para cada letra é igual entre todas as letras, dizemos que a fonte é monoespaçada. Fontes monospace não são boas para leitura de textos, mas dão destaque em legendas e principalmente códigos fonte de programação, onde a organização visual que elas promovem ajudam o raciocínio e facilitam a leitura do código fonte.

Com o advento e suporte à propriedade @font-face no mainstream, com a abundância de formatos suportados (EOT, TTF, SVG, WOFF) pelos navegadores, e também pelo suporte a propriedades gráficas de transformação (scale, skew, rotate,...) a tipografia na web alcança um novo patamar a ser explorado pelos desenvolvedores.

O conjunto

Não basta escolher a fonte visto que textos geralmente não são apenas uma palavra e sim um conjunto delas, com linhas e outros elementos em volta. Trabalhar este espaço é tão importante quanto a escolha da fonte, e na verdade influencia diretamente na escolha da fonte.

Linhas muito longas ou muito curtas são mais difíceis de ler. Algo entre 50 e 90 caracteres por linha é o recomendado para leitura em telas de computador.
O distanciamento entre as letras também influi na qualidade da leitura. Encontrar o ponto de equilíbrio, de conforto, é a chave.
O distanciamento entre linhas é outro aspecto muito importante da composição. Linhas muito próximas dificultam a leitura, muito distantes não passam a sensação de unidade, de conjunto do texto.

Estes espaçamentos mudam conforme o propósito, o público-alvo, o tipo de texto, o tamanho da fonte, entre outras variáveis. Não existe uma regra de ouro, o bom senso é quem comanda (quando for o caso de mudanças). Mas via de regra, espaçamento entre linhas em 1.5 ou 1.4 é considerado como padrão para uma boa leitura. Para o espaçamento entre letras, só mude quando houver necessidade.

Contraste

Quando ouvimos falar de contraste, logo pensamos em cor, mas contraste é um termo mais abrangente em design. É algo que quebra o padrão de repetição. As formas básicas de contraste são: tamanho, posição, textura, cor, forma e orientação. E estes se aplicam também à tipografia.
Não é somente através da mudança de fonte que podemos dar ênfase, destaque para algum elemento. Saber trabalhar com estes conceitos de contraste ajuda o design a ganhar volume e importância, mantendo a uniformidade do conjunto (sem bombardear o leitor com diversas fontes e cores diferentes), em outras palavras, criar a hierarquia visual.

Acessibilidade

Apesar de ser uma importante área do design, e existir uma infinidade de belas fontes, usar mal as fontes pode destruir o design ou pior, a credibilidade de toda a obra.
Para texto comum, de fluxo, conteúdo, use fontes com as quais as pessoas estão habituadas. Arial, Verdana, Helvetica, Tahoma, Times New Roman, Courier New, Lucida... todas estas fontes são usadas pela maioria dos grandes sites e portais, e por um motivo simples: todos estão acostumados com elas, e geralmente tem estas fontes instaladas nos seus computadores ou celulares.

E as fontes bonitas? Use elas em elementos de destaque. No logotipo, na logomarca, nos cabeçalhos, em mensagens promocionais, etc, mas não nos textos, formulários, menus e barras de navegação.

E para completar a acessibilidade, não podemos esquecer de um antigo (mas não raro esquecido ou mal usado) recurso do CSS 2.1, as famílias genéricas de fontes. O CSS prevê 5 famílias genéricas (serif, sans-serif, monospace, cursive e fantasy), que podem (devem) ser usadas nas declarações de fontes, sempre ao final, provendo uma regra de fallback. A idéia é simples, se o usuário não tem instalada a fonte que você declarou, ou mesmo se por algum motivo a fonte não pode ser baixada/processada pelo @font-face, você fornece pelo menos a família de onde a fonte pertence, e o navegador do usuário utilizará a fonte configurada para tal fim (falando sério, isso é algo bem comum de acontecer), e ainda assim, seu site manterá a aparência de conformidade para o usuário.

Se você não entendeu, deixe-me explicar melhor com um exemplo meu, real. Eu uso Linux. Não tenho instalado aqui as fontes Helvetica e Tahoma. Alguns sites, como o google code, declaram a fonte Helvetica para ser usada (font-family: Helvetica, sans-serif;). Como eu não tenho, o Firefox, o Opera e o Chrome, vão utilizar a fonte que está configurada para o tipo sans-serif, que no meu caso é a FreeSans. O site, apesar de não aparentar da forma original que os designers gostariam, ainda podem se valer desta regra de fallback, e o Google Code, assim como muitos blogs e sites que não declaram a fonte, têm uma aparência familiar pra mim, o que gera conforto visual, e passa uma sensação de confiança (não é algo bizarro ou estranho).

Então fica o lembrete: SEMPRE usar uma família genérica junto das declarações de fonte no CSS.

Resumo

  • Na maioria dos textos use fontes simples, comuns.
  • Não utlize muitas fontes, nem muitos estilos (negrito/itálico/cores) diferentes. Se necessário, reescreva o texto.
  • Use contraste como negrito, tamanho ou cores para elementos especiais ou específicos, mas procure manter a unidade e a hierarquia visual.
  • Prefira fontes sem serifa para a maior parte do texto.
  • Não crie linhas muito longas ou muito curtas.
  • Respeite o espaçamento entre linhas.
  • Sempre coloque regras de fallback.
  • Permita-se usar fontes diferentes em elementos especiais como logotipos ou cabeçalhos de seção.
  • Lembre-se sempre de ter famílias genéricas como fallback.
  • Se possível não use fontes com tamanho abaixo de 12px.
  • Use o bom senso. É melhor ficar com o lugar comum e funcional do que inventar um novo conceito que fique estranho.

Referências

On Web Typography (em inglês)
10 typography tips to bring you to the next level (em inglês)
----------- keepReading

Algumas coisas sobre PHP que você provavelmente não sabia...

Pois é, tem milhares e milhares de páginas e posts na internet com um título semelhante, e chegou minha hora de escrever um também.

Depois de alguns anos de muito código e de muito polir eles me sinto confortável pra fazer um posts desse que relatem algo realmente útil e não apenas justifique o porque usar aspas simples ou duplas no echo.

1 - Use variáveis de servidor

Vemos muita aplicação dos arrays $_GET, $_POST e $_SESSION por aí. Fora eles, dificilmente vemos algum tutorial ou artigo falando dos outros arrays, como $_COOKIE, $_REQUEST, $_ENV, $_SERVER, $_FILES....

Bom, o vetor $_SERVER[] tem muitas variáveis úteis, como SERVER_NAME, SCRIPT_NAME, REMOTE_ADDR, REQUEST_METHOD, HTTP_REFERER...

Claro que muitas dessas variáveis, como tudo quando se trata de web, podem ser manipuladas por usuários mal intencionados. Mas caso geral, são confiáveis.

Agora o bom mesmo é tirar vantagem da manipulação dessas variáveis, por exemplo para criar respostas on demand para requisições ajax. A idéia é setar uma variável no request header, que depois é lida pelo PHP. Se a variável estiver setada a requisição é Ajax.

No Javascript:
var myXHR = new XMLHttpRequest();
myXHR.setRequestHeader("X_HTTP_REQUESTED_WITH", "XMLHttpRequest");
// resto do código

No PHP:
<?php
//código...
//agora na hora da saída
if (isset($_SERVER['X_HTTP_REQUESTED_WITH'] && $_SERVER['X_HTTP_REQUESTED_WITH'] == "XMLHttpRequest") {
    echo $output;
} else {
    include("header.php");
    echo $output;
    include("bottom.php");
}
?>

2 - Constantes pré-definidas

Usar constantes do sistema deixa o código mais bonito e portável. Aprendi isso usando a constante PHP_EOL (fim de linha) para escrever uma classe de email.

Ao invés de escrever vários códigos, um para cada plataforma (o caracter é diferente para UNIX, Mac e Windows), usei apenas essa constante. Mas existem outras úteis como o PATH_SEPARATOR.

NOTA: Muitas das constantes são REALMENTE úteis para internacionalização da aplicação, juntamente das funções setlocale.

3 - Operador Ternário

Seja lá qual for a lista de top 10, 20, 382 dicas de PHP, operador ternário tá no meio. E não é a toa. Ele é simples, higiênico e poderoso.

Basicamente é a mesma coisa que um if else. Só que é interessante notar que dá pra usar ele (e aí sim ver toda sua elegância) diretamente em atribuições ou returns.
<?php
$var = 10;
echo ($var >= 10) ? 'Great!' : 'Damn!';

return (mail()) ? 'Sent with success' : 'Sorry... bad server';
?>

4 - Escreva funções

Outro jargão, mas que merece estar aqui também. Escrever funções e rotinas é o básico do básico em programação. Não adianta você querer aprender OOP, ou Design Patterns, se não sabe fazer o arroz com feijão.

Funções são simples de escrever, rápidas, poupam trabalho, limpam o código principal... Enfim, são o primeiro passo rumo a deixar de ser o sobrinho que faz sites.

5 - Extensão Filter

Extensão não muito conhecida, e menos ainda falada e comentada. Disponível built-in desde a versão 5.2 do PHP (ou seja, tem em quase todo host decente).

A extensão filter simplifica a validação e higienização de variáveis, além de fornecer funções prontas para tipos de validação comuns e/ou padronizadas. Checar se um email ou uma URL passada num formulário é válido e limpar se for o caso é muito fácil.

Claro, ela também é flexível, e fornece uma opção de callback. Então crie sua função para validar CPF, e use a extensão filter a partir de agora.

6 - Arrays

Mais um da galeria dos óbvios que estaria aqui. Bem, não tão óbvio, senão não estaria aqui. Se você já teve contato com programação mais de 3 dias na vida, já conhece os arrays. Mas o que você provavelmente desconhece são as funções para lidar com eles.

array_unique, uasort, array_intersect, array_search, array_diff, array_walk... Existe um grande número de funções úteis que geralmente por ignorância são reescritas (nem sempre da melhor forma) pelos programadores, incorrendo no caso da reinvenção da roda.

PEAR, PECL e Tuning

Ainda no tema de parar de reinventar a roda, saiba que você pode - e deveria - consultar repositórios de códigos como o PEAR, o PECL e o PHPClasses. Existem muitas soluções prontas na web para resolver problemas que você tem exatamente agora. E geralmente soluções feitas por programadores mais experientes ou mesmo por comunidades inteiras.

Além de poupar trabalho, você pode se valer disso para aprender através da leitura destes códigos, e com o tempo, pode inclusive modificar estes códigos quando for usar. Adaptar às suas necessidades ou corrigir um bug.

Conclusão

Todas as "dicas" que coloquei aqui se resumem a algumas noções não-tão-comuns sobre boas práticas.

Geralmente o que se discute quando o assunto são boas práticas e estilo são formas e padrões de se escrever o código, mas quase nunca se fala em:
  1. Aproveitar o que o sistema te oferece de forma nativa
  2. Explorar o que a linguagem tem de recursos
  3. Explorar o que já foi feito pela comunidade

Até a próxima.
----------- keepReading

quinta-feira, 16 de setembro de 2010

Como os crawlers funcionam?

Crawler, spider, bot, entre outros nomes, são programas que navegam na internet e indexam conteúdo. Os mais famosos de longe são os do google e do yahoo. Mas existem diversos, e o funcionamento básico deles é bem parecido (o que difere é como cada sistema organiza e classifica as páginas para mostrar nos resultados).

De alguma forma eles chegam à sua página, seja buscando nos bancos de DNS disponíveis na internet, ou seguindo um link. E quando chegam na sua página, eis o que eles fazem:

Primero lêem sua página, mais especificamente, o código HTML da sua página. E antes de começarem a analisar o que sua página faz ou vende, eles tentam separar os links, links internos, action de formulários... Note que eles não leem e não executam scripts, formatos de arquivo estranhos como PDF, imagens, nem nada do tipo.

Alguns buscadores é claro também indexam fotos (juntamente com o atributo alt), arquivos em flash, PDFs e outros arquivos, mas isso é outro assunto. O importante é entender que eles, antes de qualquer outra coisa, tentam descobrir o quais são as páginas dentro do seu site, e isso só pode ser feito desta forma, através do seu código HTML.

Se você possui um arquivo ou mesmo um diretório inteiro, que não esteja conectado a nenhuma página, seja através de um link, de um atributo src ou href, este arquivo ou diretório não será magicamente descoberto pelo crawler.

Bom, os crawlers do buscadores geralmente procuram informações sobre permissões sobre o conteúdo. Em espcial existem duas formas de bloquear um crawler decente de indexar uma determinada página (e os links nela contidos). A primeira forma, e mais comum, é através do arquivo robots.txt. A outra forma é através da tag meta robots, com valor "noindex" ou "nofollow", usados para não indexar (a própria página) e não seguir (os links contidos na página), respectivamente. Há também uma terceira possibilidade, muito menos explorada, que é o uso do atributo rel="nofollow" em links, indicando ao crawler que aquele link em especial não deve ser seguido.

Recapitulando... o crawler chega na sua página, lê o código HTML, procura o arquivo robots.txt, confere os links da página, confere se há alguma tag meta específica para ele, depois organiza o que ele deve seguir ou não dentro e a partir do seu site.

Ah sim, uma outra forma de um crawler indexar páginas e conteúdos é através de outros sites. Vamos supor que você tem uma página no seu site, chamada apropriadamente de secredo.html.

Se você digitar direto na barra de endereço, você acessa essa página, mas a partir de todas suas outras páginas não existe link para esta página. Certo, os crawlers nunca descobrirão esta página, de acordo com o que expliquei acima. Porém um amigo seu coloca o link desta página no site dele. Pronto, agora sua página secreta está pronta para ser indexada pelos crawlers.

Claro que usar uma restrição no robots.txt ou uma tag meta basta para que os bons crawlers não indexem esta página, afinal o autor (no caso você) explicitamente não quer que isso aconteça.

Mas nem todos buscadores são tão corretos (google, yahoo, bing, ask, baidu,... esses são). Os crawlers do mal às vezes se baseiam exatamente nas restrições para fazer descobertas e indexar conteúdo. Mas isso não é de longe motivo para alarde, afinal, a maioria desses bad crawlers não são famosos, não tem visitantes, e na maioria das vezes nem mesmo cacife financeiro para sobreviver muito tempo.

Bem, espero que eu tenha esclarecido como o processo de descoberta e indexação funciona. O processo de organização das informações como eu disse, é o que é diferente entre os buscadores.

Até a próxima.

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

Qual a diferença?

No meio do boom de possibilidades e novos recursos trazidos pelo CSS3 temos as Media Queries. E do meu ponto de vista esse novo recurso vem numa velha roupagem, trazendo junto algo que parece persistir no mundo do desenvolvimento para web.

O que são as media queries?

No CSS 2, havia a possibilidade de definir folhas de estilo diferentes para meios diferentes. O exemplo clássico é usar duas folhas distintas, uma para leitura na tela com fontes sans-serif, e uma para impressão com fontes serif, melhorando a legibilidade do conteúdo segundo onde ele for usado. O CSS 2 define alguns tipos de media, a saber:
  • all
  • braille
  • embossed
  • tv
  • tty
  • handheld
  • screen
  • print
  • projection
  • speech/aural

Com as media queries do CSS3 essas possibilidades foram estendidas para propriedades dos meios. Por exemplo, largura ou altura da tela (ou da folha), quantidade de cores, resolução, etc. Isso é media query.

Por que o estardalhaço com isso?

Resumidamente: por causa da expansão da internet 3G e nos dispositivos móveis capazes de navegar na internet.

Com as medias queries é possível detectar larguras, alturas, orientação da tela, etc, e com isso formatar o conteúdo de acordo, deixando um aspecto muito mais agradável e legível para as páginas. Seja num smarthphone, seja numa TV full HD de 30+ polegadas, seja numa folha A4, seja num outdoor.

Assim, agora as folhas de estilo poderão se adaptar aos dispositivos e meios onde serão mostradas, tudo sem nenhuma linha de script ou programação adicional, apenas com marcação HTML/XML e CSS.

Se essas media queries são um recurso tão legal, qual o problema?

O problema disso é a carga de trabalho para criação e manutenção, além da postura de alguns sites e entusiastas que proclamam esta solução como definitiva. Rotular algo como definitivo é um tiro no pé, e isso é duplamente verdade no mundo da tecnologia. Nada é definitivo porque ninguém pode prever o mercado dentro de 5 ou 10 anos.

Mas falando sobre o lado ruim do operacional, da hora do vamos ver, o problema é que as media queries podem adicionar trabalho de forma desnecessária e ineficiente. Explico. Ou você vai aumentar muito o tamanho dos arquivos CSS, ou vai criar algumas dezenas.

Como se fazia antes?

É engraçado notar que sites para dispositivos móveis existem a mais de 10 anos. Claro que a quantidade de sites aumentou muito com o aumento da participação do mercado na internet destes dispositivos, e com a evolução das tecnologias. O que antes era raro, agora é tendência. Antes era WAP, agora é HTML e CSS mesmo, só que mais enxuto.

Como mencionei antes, o CSS 2 já previa o uso de arquivos CSS diferentes, baseados em mídias e dispositivos diferentes. Uma das abordagens adotadas era - e ainda é - deixar um redirecionamento para um domínio específico ou uma folha de estilo específica quando acontecer do dispositivo se enquadrar na media handheld.

Para se ter uma idéia, o Yahoo!, o Youtube, o A List Apart, o McDonald's e o Walmart adotam esta solução ainda hoje.

Eles tem as tags
<!-- este faz o redirecionamento para um domínio separado -->
<link rel="alternate" href="http://m.domain.com" media="handheld" />
ou
<!-- este aponta para uma folha de estilo separada -->
<link rel="stylesheet" type="text/css" href="mobileStyle.css" media="handheld" />

Abordagens alternativas e seus pontos positivos e negativos

Estas claro que não são as únicas soluções, mas são as mais difundidas atualmente. E com as Media Queries do CSS3, surge uma leva de críticos teóricos a este modelo. Uma das críticas é que o conteúdo fica espalhado. Vários domínios, várias folhas de estilo... É ruim para o controle, etc...

Frente ao mercado atual de tecnologias disponíveis e de dispositivos/suporte, temos algumas soluções que fazem sentido e outras nem tanto.

A primeira seria usar apenas o atributo media para importar um arquivo CSS para handhelds. É uma solução muito difundida, talvez a mais difundida. É eficiente, testada, funcional. O problema é que as capacidades variam de acordo com os dispositivos móveis. Largura, altura, resolução, cores e orientação são as mais significativas em termos de design (por isso as media queries foram criadas).

Outra solução é adotar um subdomínio diferente. Esta solução também é bastante popular, mas recebe duras críticas. O motivo é simples. Se para cada faixa de resolução for criado um novo subdomínio e uma nova folha de estilo, isso pode gerar um monstro dentro de alguns anos. Bem, isso é um ponto de vista dos teóricos. Na prática, esta abordagem traz alguns outros benefícios, como o lado do servidor, que pode processar e gerar respostas mais leves (usar outras imagens, remover flash, javascript, anúncios...), o que é muito interessante para quem usa dispositivos móveis, afinal é consumo de banda menor, download e processamento mais rápidos.

Usar media queries para importar diferentes estilos. A idéia queridinha do momento. Mas ninguém pensa (ou apenas ignora, ou dá importância menor) no fato de que neste caso, várias folhas de estilo são necessárias, o que representa um problema tão grande para a manutenção quanto ter um domínio separado. HTML com cabeçalho maior para TODOS os casos, e uma trabalheira muito grande para os desenvolvedores e designers.

Usar media queries dentro do arquivo CSS para criar regras específicas para determinados dispositivos. Parece interessante, mas o arquivo ficaria simplesmente gigante, com uma variação de regras repetidas várias vezes. Continua uma solução estranha, com um arquivo de CSS ruim de dar manutenção, e extremamente grande pra baixar e lento pra processar.

Usa ou não usar?

Faladas as soluções (ou futuras tendências de), hora de pesar cada coisa, e de escolher uma abordagem. Claro que isto irá variar de caso para caso, mas no geral, as minhas consideração estão abaixo.

Usar um (ou mais, caso necessário) domínio alternativo, e fazer uso de redirecionamentos com a tag link. Um para dispositivos móveis, um para desktops normais, e talvez um para monitores grandes e TVs. Eu sou a favor do uso de domínios alternativos principalmente pelo fato de poder valer-se de programação server-side e fornecer conteúdos diferentes, economizando banda e outros recursos dos clientes.

Para cada domínio, um CSS diferente. Apesar de parecer mais complicado, é mais leve, depois de um arquivo CSS pronto, basta duplicar o conteúdo e adicionar/subtrair algumas regras.

Ah sim, nestes arquivos CSS diferentes sim usar as media queries. Continua aumentando o número de regras, mas é muito mais conveniente e leve fazer desta forma, ao invés de servir um único arquivo CSS grande.

Conclusões

Media queries são um novo recurso muito interessante e poderoso sem sombra de dúvidas. Mas do meu ponto de vista ele não resolve perfeitamente a condição de ter que filtrar de alguma forma informações do cliente. Aliás, esta realidade de exibir páginas diferentes (layouts diferentes) é algo que depende do mercado de monitores, dispositivos, browsers, e não tem como servir um conteúdo único, apenas um arquivo html e um arquivo CSS, que enquadre todas as soluções de forma otimizada. No passado e no presente bibliotecas javascript serviram a este propósito, agora temos como fazer isso em CSS.

Antes não haviam padrões W3C para realizar este tipo de tarefa, e sempre que o assunto pende para este lado, surgem defensores fanáticos ressaltando todos os pontos ruins de uma solução em javascript (não é standard, e não serve para todos os dispositivos). Apesar de ter um fundo de verdade nesta afirmação, na prática 99.8% do fluxo na internet vai para navegadores que contam com esta tecnologia, daí o porque deste fanatismo ser exacerbado em meu ponto de vista (que sou um standardista).

Agora temos as media queries, um jeito W3C de fazer essa diferenciação para dispositivos diferentes. No entanto, a necessidade de ter marcações ou regras diferentes para dispositivos diferentes continua existindo, assim como o trabalho de criar regras diferentes.

O fiel da balança, junto desta necessidade de servir conteúdos/formatos diferentes, é o usuário. Se por um lado temos como servir estes conteúdos, o forma de serví-los pode não ser otimizada. Por que ter um aumento de 150% no tamanho do arquivo CSS, sendo que pode-se criar um domínio separado, e servir arquivos menores (inclusive o output HTML menor)? O que é melhor para os usuários? E para os desenvolvedores? Será que criar mais de uma folha de estilo é tão complicado assim, ou seria melhor para fazer manutenção?

Sou a favor do uso das media queries, mas não como solução única, absoluta e ótima. Cada caso deve ser analisado, e outros fatores devem sim ser levados em consideração, principalmente no que diz respeito aos usuários.
----------- keepReading

terça-feira, 14 de setembro de 2010

Web Semântica: HTML5 e Microformats

O que é Web Semântica?

No início da internet, tivemos o HTML para criar e marcar o conteúdo. Com ele, que é usado até hoje em todas as páginas existentes, é possível criar textos, links, inserir imagens e tabelas, formulários, e muito mais. Porém a classificação e o uso destes elementos permite apenas funcionalidade e não significado. Um link geralmente assume a forma de um texto ou imagem, que uma vez clicado leva a outra página, mas não diz o significado, a natureza dessa relação. Adicionar significado à relação dos elementos é a idéia principal por trás da semantica na web.

Por que é importante?

Para os usuários a diferença não é tão transparente ou mesmo útil no dia a dia. Pouca ou nenhuma funcionalidade nova é agregada para a intereção do usuário. Mas para os buscadores e indexadores, isso faz toda a diferença, e isso sim acaba afetando a qualidade da navegação dos usuários, além das relações comerciais (ou não) entre os próprios sites. Um exemplo seria procurar restaurantes, com determinada faixa de preço, com boa avaliação dos frequentadores, em determinada região. Outro exemplo seria procurar todos os artigos de um determinado autor que estão publicados sob determinada licensa de uso.

Hmm... será que isso pega?

Essa idéia é relativamente antiga. Nenhum buscador tinha esse tipo de tecnologia, por isso ninguém investia em desenvolver padrões ou criar sites com esses recursos. Hoje os buscadores (Yahoo!, Google, Bing entre outros) já tem suporte a ler e classificar essas informações. O que está em falta são sites que implementem esses recursos. Os que implementam certamente ganham destaque (e todos os grandes sites já implementaram ou estão implementando).

Não existe um único formato de se adicionar significado aos elementos. Dos padrões mais conhecidos temos o RDFa e os Microformats, com uma certa vantagem em maturidade e simplicidade para este último. Mas a semantica ganhou mais força junto com o suporte dos buscadores, e com o HTML5 (que é o grande preferido entre os buscadores agora).

Se faltam páginas com microformats, falta ainda mais páginas feitas em HTML5. E pra completar a história, o HTML5 já preve o uso de muitos microformats, entre outros mecanismos úteis para adicionar significado de uma forma ordenada.

#Comofas?

A semantica é adicionada através de atributos dos elementos. Os microformats usam geralmente o atributo class, mas também se valem em alguns casos dos atributos id e rel.

Como este post NÃO É um tutorial, por hora recomendo a leitura das especificações: HTML5 Metadata, HTML5 rel values, Microformats.

O único ponto de conflito que vejo entre microformats e HTML5 é o uso do atributo rel. O HTML5 define uma lista de possíveis valores que podem ser usados (e alguns valores sugeridos pelos Microformats não estão nesta lista). O jeito de contornar estes conflitos, mantendo consistência com a especificação do HTML5, seria adotar o mecanismo de metadados do próprio HTML5 para fazer essas marcações ao invés de usar os Microformats. No mais, geralmente os dois, Microformats e HTML5 são completamente compatíveis.

Conclusão

A web semântica é vista como tendência futura a muito tempo, e com o impulso dado pelos buscadores e pelo HTML5, espero que os desenvolvedores comecem a adotar o uso dos mesmos em breve.

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

sábado, 4 de setembro de 2010

Criando bordas semi-transparentes com CSS3 (ou CSS3 glass effect borders)

Criando bordas semi-transparentes com CSS3

O CSS3 está aí, junto com o HTML5, e mesmo que ainda não seja seguro ou mesmo viável pela disponibilidade de suporte, usar todos os recursos, existem muitos efeitos que podem ser usados a partir de agora.



O que vamos fazer aqui não é nada novo, afinal, as bordas arredondadas ou molduras estão presentes na maioria dos sites, mas a criação das mesmas ainda é feita de forma que a partir de agora é desnecessariamente complicada. O CSS3 traz um jeito clean de se alcançar estes efeitos.

Onde cai bem?
Lightboxes, overlays, mega dropdowns... elementos de destaque em geral.

A marcação
<div id="moldura">
<div>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum vitae venenatis nisl. Nulla eu convallis ante. Nam non lectus mi. Proin porta, ante eu aliquam vestibulum, ligula sem pellentesque lectus, sit amet pulvinar dolor neque vel arcu. Duis id tellus eu purus blandit adipiscing. Phasellus malesuada rhoncus elit id semper. Curabitur mollis aliquam elit. Aenean in enim vitae massa pretium viverra vitae sit amet ipsum. Donec in lorem felis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.</p>
</div>
</div>

O estilo
* {margin:0px; padding: 0px;}
.moldura {width: 360px; border: 8px solid rgba(0,0,0,0.3); -moz-border-radius: 10px; border-radius: 10px;}
.moldura div {background-color: rgba(255,255,255,0.7); padding: 0.5em;}

Isto funciona bem para o Firefox 3.5+, Chrome, Safari e Opera. Como podemos ver as regras necessárias para criar o box e estilizá-lo são bem simples. Fazemos uso apenas de border-radius e cores rgba dos recursos do CSS3. Note que apesar de tudo, ainda temos duas divs (o que semanticamente não é tão correto assim, mas é melhor que as soluções anteriores, onde haviam 10 ou mais divs aninhadas). Isto por causa do box model. Quando usamos apenas uma div a cor de fundo da div se mescla com a cor de fundo da borda, tirando o efeito desejado.

Para o IE, o border-radius não funciona - pra variar. Antes de continuar com a seção dos poréns, vale dar uma lida no meu artigo anterior.

OK, hora das regras de fallback. Para as bordas a regra mais acessível é ligeiramente diferente de background. Para a cor de fundo vamos usar a seguinte cadeia de regras: cor sólida, imagem, cor rgba. Para bordas utilizaremos cor sólida e cor rgba. Isto porque os navegadores que suportam imagens de fundo para bordas, através da propriedade CSS3 específica border-image, via de regra aceitam cores rgba.

Então reescrevendo as regras com os fallbacks (na folha de estilo exclusiva para IE) temos:
.moldura {border: 8px solid #000;}
/* criamos uma imagem PNG de 1x1 pixel de cor branca, e com opacidade de 80% */
.moldura div {background-color: transparent; background-image: url('white_80.png');}

Até a próxima

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

Por que tudo igual?

Este artigo é inspirado nos artigos Ignorance Is Bliss, de Andy Clarke, e Make Your Mockup In Markup, de Meagan Fisher, publicados no 24ways.

Um dos pontos mais problemáticos no desenvolvimento de um site, ao lado da acessibilidade, é o layout. Em especial fazer com que o site tenha exatamente a mesma aparência em todos os navegadores, em todas as plataformas e em todas as resoluções de tela.

Para esta tarefa, esta meta, não preciso dizer que além de muito trabalho, limita o desenvolvedor nos recursos que podem ser utilizados e na infinidade de hacks necessários. Este ideal é praticamente utópico e conflita diretamente com dois interesses: o do desenvolvedor de realizar um trabalho de qualidade (criar uma solução efetiva, padronizada, acessível) e com o do cliente, porque influi diretamente em prazos e custos.

Pois bem, em primeiro lugar, se você ainda cria algo no Photoshop ou qualquer outro software de processamento de imagem, fornecendo uma foto do trabalho que será realizado ao cliente, pare agora. Quando você fornece uma foto, uma imagem, você cria expectativas no cliente, e mais tarde quem vai ralar para conseguir atingir esta expectativa a qualquer custo é você mesmo.

Para o processo de aprovação do layout, use um site. Crie um site, sem efeitos, sem interatividade, e esclareça isso ao cliente. Diga que fará um site, sem programação ou interatividade porque isto demandam trabalho, e que não é a versão final, é apenas para avaliação estética, do layout.

A partir de então, faça a marcação em HTML bem feita, e trabalhe o CSS. Pronto. Se você fizer, utilizando o Firefox ou o Chrome, e o cliente ver no Firefox, ótimo. Se ele ver no Internet Explorer, mas mesmo assim aprovar, não tem porque você gastar horas polindo alguns detalhes mínimos.

Trabalhando desta forma, sem gerar expectativas complicadas no cliente, você reduz o trabalho sobre detalhes, aumenta sua produtividade, a qualidade da produção e, de quebra, sua competitividade.

Muitas vezes somos tão aficcionados pela perfeição, que esquecemos o que realmente importa no negócio: a satisfação dos usuários. Usuários comuns não abrem um site, e depois abrem ele novamente em outro navegador só para conferir como ficou o layout, nem redimensionam a janela compulsivamente afim de achar um elemento quebrado. Usuários comuns leem o que querem ler, falam o que querem falar, compram o que querem comprar, fazem o que querem fazer. Se o site lhes dá os meios suficientes para eles completarem suas tarefas, ótimo, isso lhes basta. Quanto mais simples e direto for o processo que leva o usuário atingir seus objetivos, melhor. Nenhuma borda arredondada, ou fonte, ou efeito visual é tão importante ao julgamente quanto a facilidade de uso.

Bem, é isso. Chega da busca da perfeição milimétrica a qualquer preço, e mais respeito às normas e aos usuários.

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

quinta-feira, 2 de setembro de 2010

Evolução dos navegadores... de novo

No começo de 2008 ouvi o burburinho sobre o navegador do Google, o Chrome. Baixei para experimentar. Minha primeira impressão foi: é a mistura do Opera com o Firefox. Visual clean, alguns recursos de customização, mas fora o rápido engine de javascript, o resto era MUITO imaturo ainda. Faltavam muitos recursos para poder ser considerado um browser do mainstream, mas sendo do google, isso era apenas uma questão de tempo. Abandonei ele até agora. Testei a poucos dias a versão 5 do Chrome. Muito melhor. Mas créditos não só ao google, mas também ao pessoal do Webkit.

Esse mercado de browser eu acompanho, ainda que não tão de perto, bastante, assim como os padrões da web. E isso me surpreende bastante. Tanto pela inovação das partes, quanto pela velocidade das mudanças.

Abertamente, Opera, Firefox, Safari e Chrome trazem a cada release novidades interessantes. O Opera com o layout clean e recursos embutidos, o Safari com o anti-aliasing das fontes, o Firefox e o Chrome dispensam maiores apresentações. Uma mudança que me chamou muito a atenção foi o Opera e o Webkit (Chrome e Safari) embutirem ferramentas para desenvolvedores, no melhor estilo Firebug. Certamente eles focaram o mercado de quem leva a web a sério. Gostei.

Mas o que mais me chama a atenção é a velocidade das releases (e a adoção dos novos padrões, HTML 5, CSS 3, novas APIs de Javascript). De 2008 pra cá, o Opera saiu da versão 8 para a versão 10. O Chrome, saiu da versão 1 para a versão 5, o Firefox, da 2.5 para a 3.6. Todos eles implementam atualizações menores com uma frequencia praticamente mensal, e pelo menos duas atualizações significativas por ano.

Já o nosso amigo Internet Explorer está tentando dar adeus à versão 6, que já existe há 10 anos (e conta com o "apoio" do Google que declarou o fim do suporte para a problemática e custosa versão do IE.). A versão 7 e a versão 8 dividem o mercado, e estão bem atrás da concorrência em termos de inovações tecnológicas. O IE domina o mercado, mas já sente a pressão, principalmente porque quem usa mais do que orkut e MSN na internet, tende a procurar um navegador com mais recursos. Isso mostra que esse público, que é economicamente interessante, já percebe a falta de qualidade e de opções das versões atuais do internet explorer, e a falta de atualizações por parte da MS para o IE é um problema, que a Microsoft vem trabalhando sério, mas ainda falta o ritmo dos concorrentes.

Para ser sincero, gostei de todas as iniciativas de todas as partes. Na busca de trazer resultados para os grupos que realmente importam no fim das contas: os usuários e os desenvolvedores. E o fiel da balança, o Internet Explorer, já mostra sinais de que tende a rumar para o mesmo caminho, lentamente como sempre, mas já tomou um norte e em questão de 4 ou 5 anos, creio que o cenário estará ainda melhor.

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

quarta-feira, 18 de agosto de 2010

Canto da segurança: Fixação de sessão

Continuando com a seção de traduções de artigos, lá vem mais um do Chris Shiflett, complementar à tradução anterior. Lá vai:

Canto da segurança: Fixação de sessão


Segurança está ganhando mais e mais atenção entre os profissionais de PHP. Como o PHP continua sendo um componente chave do futuro da web, atacantes maliciosos começaram a atacar fraquezas nas aplicações em PHP de forma mais frequente, e os desenvolvedores precisam estar prontos.

Eu estou muito contente de introduzir o Canto da Segurança, uma nova coluna mensal que é completamente focada em segurança no PHP. A cada mês discutirei um tópico importante com grande detalhamento que pode ajudar a aumentar a segurança de suas aplicações em PHP e a se defender de vários tipos de ataques. Estes tópicos não serão vagos, introduções gerais, então se você está procurando por uma introdução sobre segurança em aplicações PHP, você estará melhor servido por outras fontes de informação, como o capítulo sobre segurança do manual do PHP.

O tópico deste mês é Fixação de Sessão, um método de obter um identificador de sessão válido, sem a necessidade de adivinhar ou de capturar um. O nome deste tipo de ataque se originou de uma publicação da Acros Security entitulado Session Fixation Vulnerability in Web-based Applications, embora este método por si próprio seja anterior à publicação. Irei expandir a idéia básica de fixação de sessão e demonstrar alguns métodos de prevenção, tudo dentro do contexto específico do PHP.

Segurança de sessões é um tópico vasto e complexo. Um dos princípios fundamentais da segurança em aplicações web é nunca confiar nos dados vindos do cliente. Contudo, com o objetivo de atingir o estado completo, o cliente deve identificar a si mesmo enviando um identificador único. Este conflito fundamental cria complexidades significantes pra os desenvolvedores que querem construir aplicações de estado completo e seguras. De fato, o mecanismo de sessões em qualquer aplicação web é a característica mais vulnerável da aplicação, e segurança de sessões é um dos tópicos mais complexos das aplicações web em qualquer plataforma.

Existem numerosos tipos de ataques baseados em sessão. Muitos dos quais se enquadram numa categoria chamada personificação (roubo de sessão), onde um usuário malicioso tenta acessar a sessão de outro usuário, tentando se passar por aquele usuário. No fundo, estes tipos de ataque necessitam que o usuário malicioso obtenha um identificador de sessão válido, porque esta é a quantidade de informação mínima que deve ser usada para a identificação.

Existem, pelo menos, três formas para que um identificador de sessão válido seja obtido por um atacante:

  1. Predição
  2. Captura
  3. Fixação

Predição envolve apenas adivinhar um identificador de sessão válido. O intervalo desta adivinhação pode variar desde chute até uma mais certeira, dependendo da sofisticação do ataque que se usa. Com o mecanismo de sessões nativo do PHP, identificadores de sessão válidos são extremamente difíceis de predizer, então este não está para ser o ponto mais fraco da sua implementação.

Capturar um identificador de sessão válido é muito mais comum, e existem numerosos tipos de ataque que usam essa abordagem. Quando um cookie é usado para armazenar o identificador de sessão, uma vulnerabilidade no browser pode ser explorada para se obter o identificador de sessão. Quando uma variável de URL é usada, o identificador de sessão é mais exposto, e existem muitos métodos potenciais de captura. Por esta razão, cookies são geralmente considerados mais seguros do que variáveis de URL para a propagação de identificadores de sessão, embora as preferências de usuário devem ser respeitadas, e vulnerabilidades de browser existam em todas as versões do browser mais popular, Internet Explorer (veja peacefire.org/security/iecookies/ e Passport Hacking Revisited para mais informações).

Fixação de sessão é um método que engana a vítima para ela usar um identificador de sessão escolhido pelo atacante. Se bem sucedido, ele representa o método mais simples no qual um identificador de sessão pode ser obtido.

Um simples ataque


No mais simples caso, a fixação de sessão usa um link:

<a href="http://host/index.php?PHPSESSID=1234">
Click here
</a>

Ou um redirecionamento no nível de protocolo:
<?php
header('Location: http://host/index.php?PHPSESSID=1234');
?>

Outro métodos incluem o cabeçalho Refresh, seja passado como um legitimo cabeçalho HTTP ou usando o atributo http-equiv da tag meta. O ponto é fazer o usuário visitar uma URL remota que inclua um identificador de sessão escolhido pelo atacante. Este é o primeiro passo num ataque básico, e o fluxo completo do ataque é ilustrado na figura 1.

Figura 1.

Se bem sucedido, um atacante está apto a evitar a necessidade de capturar ou predizer um identificador de sessão válido, e é subsequentemente possível lançar ataques de outros tipos e mais perigosos.

Você acha que não está vulnerável? Considere o código da Lista 1. Salve este código como session.php em algum lugar que você possa testá-lo. Depois que você se assegurar que não existem cookies do mesmo servidor (limpe todos os cookies caso não tenha certeza), use a URL terminando em session.php?PHPSESSID=1234 para visitar a página. Por exemplo, http://host/session.php?PHPSESSID=1234. O script deve escrever 0 na sua tela após a primeira visita. Recarregue a página algumas vezes, e você irá notar que o número aumenta cada vez, indicando o número de visitas.

Lista 1
<?php
session_start();

if (!isset($_SESSION['count']))
{
$_SESSION['count'] = 0;
}
else
{
$_SESSION['count']++;
}

echo $_SESSION['count'];
?>

Com um outro browser, ou mesmo com um computador completamente diferente, refaça os exatos passos iniciais. Após visitar a URL pela primeira vez, você perceberá que não vê 0. Mais que isso, ele chama a sua sessão prévia. Assim, você personificou o usuário prévio. Agora, se você considerar que tudo isso começou com um identificador de sessão sendo passado na URL, você pode ver o perigo básico que a fixação de sessão apresenta. Diferente de um cenário típico, o PHP não gerou o identificador de sessão.

Existem poucas falhas neste tipo de ataque simplista. A falha mais importante é que a aplicação alvo deve usar o identificador de sessão passado a ela, senão este ataque falhará. Se o seu mecanismo de sessões não é nada mais do que session_start(), suas aplicações estão vulneráveis como a demosntração prévia ilustra. Para prevenir este tipo específico de vulnerabilidade, você deve sempre se assegurar que um novo identificador de sessão é usado sempre que está iniciando uma sessão pela primeira vez. Existem muitas formas de se alcançar isto, e um exemplo é dado na Lista 2 (esta abordagem, também, tem pelo menos uma brecha, então espere até o você finalizar este artigo antes de decidir qual solução se enquadra melhor nas suas necessidades).

Lista 2

<?php
session_start();

if (!isset($_SESSION['initiated']))
{
session_regenerate_id();
$_SESSION['initiated'] = true;
}
?>

Se o código da lista 2 é usado para iniciar todas as sessões, qualquer sessão existente sempre terá uma variável de sessão iniciada que sempre está configurada. Se não é o caso, é uma nova sessão. A chamada a session_regenerate_id troca o valor atual do identificador de sessão por um novo, embora ainda retenha as informações antigas da sessão. Então, se um atacante força um usuárioem usar um link externo que contenha um identificador de sessão para sua aplicação, esta abordagem previnirá o atacante de saber o novo identificador de sessão, a menos que está sessão já esteja iniciada.

Um Ataque sofisticado

Um ataque mais sofisticado de fixação de sessão é um onde o atacante primeiro inicia a sessão no site alvo, opcionalmente mantém a sessão de incorrer em expiração, e então executa os passos mencionados previamente.

Uma alternativa à abordagem utilizada na Lista 2 é chamar session_regenerate_id() sempre que um usuário faz login de forma bem sucedida, a partir deste momento os dados de sessão se tornam sensíveis para a maioria das aplicações. Por exemplo, sempre que você valida o nome de usuário e a senha de um usuário, você pode configurar uma variável de sessão que indica sucesso:

$_SESSION['logged_in'] = true;

Apenas configurando tal vairável de sessão antes, uma chamada para session_regenerate_id() pode ajudar a proteger contra um ataque de fixação de sessão:

session_regenerate_id();
$_SESSION['logged_in'] = true;

De fato, uma boa abordagem é sempre regenerar o identificador de sessão sempre que o privilégio do usuário mudar de alguma forma, incluindo situações onde o usuário deve re-autenticar devido a expiração. Fazendo isso, você pode estar certo que a vulnerabilidade de fixação de sessão não é o aspecto mais fraco do seu mecanismo de controle de acesso.

Esta abordagem é mais segura do que o exemplo anterior, porque ela adiciona outro obstáculo significante para um atacante transpor, e previne ataques sofisticados, onde o indentificador de sessão é primeiro criado e mantido. Infelizmente, há ainda pelo menos uma fraqueza, embora o design da sua aplicação já deve previni-la.

Um ataque avançado


No mais avançado tipo de ataque de fixação de sessão, o atacante primeiro obtém uma conta válida na aplicação alvo e isto é tipicamente apelativo apenas quando o atacante pode fazer isso anonimamente. Em algumas aplicações PHP, a página de login é um script separado, como login.php, e este script pode não checar o estado do usuário, porque parece seguro assumir que o usuário não foi autenticado ainda.

Pelo contrário, esta abordagem permite que um atacante crie uma sessão, entre na aplicação com aquela sessão, opcionalmente evitando que a sessão expire, e usando a URL da página de login para lançar o ataque. Se o login. Se a página de login aceitar o novo login de usuário mas falhar para regenerar o identificador (porque o nível de acesso não mudou), a vulnerabilidade existe.

Este cenário pode parecer improvável, mas uma análise aprofundada do seu código com esta sitaução em mente vale o tempo investido. Existem dois métodos fáceis de prevenir este caso em especial:

  1. Faça a página de login sempre reconhecer o estado do usuário.
  2. Sempre regenere o identificador de sessão no script receptor, independente do estado do usuário.

Até a pŕoxima...


Uma recomendação genérica boa para prevenir ataque de fixação de sessão é regenerar o identificador de sessão sempre que o usuário providenciar informações de autenticação de qualquer tipo. Desconfie de repassar as sugestões simplistas "pega-tudo", contudo, por causa que más interpretações são mais prováveis quando alguém não está familizarizado com o tipo de ataque a ser prevenido. Não há bom substituto para um bom entendimento da fixação de sessão, e é possível que a melhor prevenção para suas aplicações não esteja nem mencionada neste artigo.

Felizmente, agora você pode eliminar a fixação de sessão da sua lista de riscos sérios à segurança com os quais você deve se preocupar. Se você desenvolver um método de prevenção particularmente criativo, eu adoraria ficar por dentro. Até o próximo mês, e fiquem seguros.

CSS Hacks, IE e Standards

Não, este não é um post para falar de como os outros navedores são mais legais que o Internet Explorer, e sim sobre como fazer uma "gambiarra elegante".

OK, não fui completamente sincero. O IE carece sim de muitos recursos e suporte que os outros navegadores dispõe, e isso faz diferença para quem trabalha com web. O que poucos desenvolvedores e webdesigners sabem na verdade é que o IE tem muitas soluções próprias para muita coisa. Sim, concordo que essas soluções não estão previstas nos padrões, mas o fato aqui é que elas existem.

Vou um pouco além e faço um questionamento sobre os próprios padrões e a implementação nos navegadores. Os novos - e bonitos - recursos de CSS3 incluem bordas arredondadas, sombras, cores com transparência (RGBA e HSLA) entre muitos outros. Pois bem, a nova safra de navegadores traz suporte a esses recursos, mas... nem tudo é tão fácil assim. Muitas das propriedades são implementadas através de:

myElt {
-moz-someNewStandardProperty: 20px;
-webkit-someNewStandardProperty: 20px;
-khtml-someNewStandardProperty: 20px;
-o-someNewStandardProperty: 20px;
someNewStandardProperty: 20px;
}

Sim, o padrão prevê abertura para implementações proprietárias através de prefixos (-moz, -o, -webkit,...). Assim, navegadores modernos como o Firefox, Chrome, Safari e Opera podem desconhecer completamente uma regra padrão, mas ter uma implementação proprietária para o mesmo. A exemplo das bordas arredondadas. Somente o Opera é que tem suporte à declaração nativa, prevista no padrão. Todos os outros tem implementações proprietárias (não foge do padrão por este mecanismo estar previsto, mas não é a mesma propriedade).

No meu ponto de vista, isso é uma coisa boa, mas também me parece uma gambiarra elegante e regulamentada.

O IE oferece alternativas a muitos desses recursos (não a todos, eu sei), e também a muitos recursos que os outros nem sonham em ter. O fato é que muitos desses recursos no Internet Explorer usam declarações completamente proprietárias. Da versão 7 pra cima, algumas propriedades já puderam são reconhecidas através de declarações com prefixo -ms-.

Enfim, vamos à minha sugestão de como fazer as coisas. Os benefícios são:
- Mantém o código válido (W3C)
- Segue recomendação da Microsoft
- Agiliza o processo de desenvolvimento

Primeiro, desenvolva o trabalho como de costume, SEM DAR A MÍNIMA PARA O LAYOUT NO IE, mas por favor, marcação semântica, enxuta, inteligente.
Depois, hora de alinhar as coisas no IE, mas sem sujar o código que já está feito e funcionando. Para isso, use a marcação no final (depois das suas declarações de CSS e Javascript) do seção <head>:

<!--[if IE]>
<script type="text/javascript" src="/scripts/ie.js"></script>
<link rel="stylesheet" type="text/css" href="/styles/ie.css" />
<![endif]-->

Esse código é um comentário, ou seja, será ignorado por outros navegadores e pelo validador W3C. Mas é reconhecido - e recomendado - pela Microsoft/IE. Ótimo, assim podemos apenas adicionar as regras, efeitos e hacks necessários apenas nestes arquivos.

Alguns de vocês podem conhecer esta técnica, e alguns podem até sugerir que se faça uso das comparações avançadas (que também são recomendadas pela MS), como [if gte IE 7] ou [if IE 8], mas neste ponto eu já discordo. Ao invés de usar várias folhas de estilo e/ou vários scripts, sugiro usar regras de fallback num arquivo único. É simples, funcional, e diminui a quantidade de arquivos a serem baixados e mantidos facilitando a manutenção.

A idéia de fallback pra quem não está familizarizado consiste em declarar as propriedades mais antigas, ou não suportadas DEPOIS das regras mais aceitas. Assim, se a última regra falha, o navegador mantém a regra válida anterior.

A produtividade aumenta porque se evita o trabalhoso processo de modificar e testar repetidas vezes até que o resultado desejado seja obtido. Se trabalha uma vez, se faz os ajustes uma única vez também, se a preocupação de quebrar o layout em outros navegadores.

Bem, é isso. Espero que os códigos de vocês fiquem mais limpos, e que o processo de desenvolvimento seja mais rápido.
----------- keepReading

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