terça-feira, 31 de julho de 2012

O Mito da Bala de Prata

Não é raro, quando estamos conversando sobre gestão, tecnologia e  afins que alguém cite alguma coisa que leu de um livro desses sobre gestão com uma certa cara de auto ajuda. Não que eu tenha algo contra as pessoas lerem sobre o tema em que trabalham, ao contrário, considero isso fundamental. Mas, algumas pessoas compram a idéia  vendida por alguns livros como uma bala de prata que vai garantir o seu sucesso.

Se existe algo que não é garantido é o sucesso. Não existe formula garantida para se chegar ao topo e mesmo que houvesse ela certamente não estaria num livro, ainda mais um vendido para um milhão de pessoas pelo mais óbvio dos motivos: um milhão de pessoas não podem estar no topo ao mesmo tempo.

Talvez, caso algo no livro realmente o livro seja realmente revolucionário, conhecer isso não vai garantir o seu sucesso, não conhecer é que talvez, garanta o seu fracasso. Tal qual a evolução dos carros de corrida de Formula 1 toda grande sacada que fez com que alguma bandeira conseguisse ficar na frente por algumas corridas vai naturalmente sendo descoberta e copiada pelas demais. Até o momento que aquele elemento deixou de ser um diferencial ele se tornou essencial. Algum tempo depois acabou se tornando ultrapassado.

Muitos dos livros tentam vender essa idéia absurda de que existe o caminho garantido para o sucesso seguindo os 10 passos do empreendedor, compreendendo os 21 segredos do Steve Jobs, falando as 10 palavras developers do Steve Ballmer, ou mantendo o mesmo 1 olhar como Steve Seagal. Então, para tentar entrar na onda, eu vou listar as 5 más notícias irrefutáveis de Thiago Mata:

  • Você sempre tem empresas concorrentes. Se você não conseguiu ainda descobrir, tenho um segredinho fácil: Veja com o que seus clientes (potenciais ou reais) estão gastando o dinheiro.
  • Seus concorrentes aprendem. O seu diferencial se tornará menos diferencial com o passar do tempo. Para se manter na corrida você precisará estar conseguindo se destacar novamente, sempre. Os seus concorrentes vão notar o mercado que você tomou deles e vão fazer o possível para compreender qual é o seu diferencial, incorpora-lo e aperfeiçoa-lo.
  • Seus clientes mudam. O que seu cliente gosta hoje pode não gostar amanha. O que é suficiente para ele hoje não será amanha. Se seus produtos não se adaptarem vão se tornar uma lembrança do passado. As necessidades mudam, a economia muda, tudo muda e nada é garantido.
  • Não existe "pé de dinheiro". Não existe um investimento, um truque, um método qualquer que gere retorno garantido por período indeterminado. Seja investindo em imóveis, comprando ações, ouro, títulos do tesouro, capital de risco, etc. todo o poço tem seu fundo. Todo investimento tem seu risco e sim tudo pode dar errado mesmo que você tenha seguido a risca a cartilha.
  • A idéia que pode mudar o seu mundo, ainda está por vir. Quando lemos a sacada genial que fez com que uma empresa se tornasse um referencial de sucesso, essa sacada já era. Ela já caiu no popular e não é mais um diferencial. Para a sua empresa conseguir se tornar o próximo referencial de sucesso cabe a você ter a próxima grande sacada. Caso bem sucedido, os livros vão estar falando sobre a sua sacada no futuro. Claro que estudar os sucessos e fracassos do passado podem te auxiliar, mas a responsabilidade de ter essa sacada é somente sua, não vai estar escrita em nenhum livro - ou blog.
Então, assim que você terminar de ler aquele livro que te garante que é bem fácil criar a empresa mais bem sucedida do mundo, repita esses itens até voltar a realidade. Lembre-se de que todos esses grandes sucessos e fracassos do passado preparam o mundo para você e agora é a sua vez de jogar. Boa sorte!

quinta-feira, 17 de maio de 2012

Gambiarra é Empréstimo de Produtividade.


Recentemente, enquanto conversava com meu amigo Raphael sobre "soluções de caráter paliativos e temporário mas que serão utilizadas por período indeterminado", mais conhecidas como gambiarras, e como o mal uso dessas pode se acabar com um projeto, surgiu um comparativo que acredito que pode ajudar muito pessoas que não são desenvolvedoras mas que, por algum motivo, interagem com a área de desenvolvimento. Pronto? lá vai...

Gambiarra é Empréstimo de Produtividade.

Imagina que você quer comprar um carro novo mas não tem dinheiro o suficiente para comprar um agora. Você tem duas opções:

  • Espera alguns meses e com um pouco do salário vai fazendo uma poupança para comprar;
  • Pega um empréstimo, começa a usufruir do carro hoje, mas vai ter que ficar pagando nos próximos meses.


Na primeira opção você precisa esperar um bom tempo, até que o total de dinheiro acumulado seja suficiente para a compra. Não há dívidas e o carro quando entregue é 100% seu. Em compensação você teve de esperar muito tempo para ter o seu carro e não é raro, que você precise do carro com urgência.

Na segunda opção, você conseguiu resolver o problema da urgência. O carro que você precisava foi entregue e você pode usufruir deste enquanto pagava mas isso não foi de graça. O empréstimo tem juros o que vai fazer com que, muitas vezes, você acaba pagando dois carros para ter um.

Empréstimos são parte da nossa vida. Mas, repetindo o que todo economista está cansado de dizer é preciso saber usar. Se ao fazer um empréstimo você não faz um planejamento de pagamento deste, o dinheiro do mês que vem não será suficiente para as suas necessidades diárias e então você vai ter de pegar outro empréstimo, ou se preferir entrar no cheque especial de novo. Esse ciclo de dívidas apenas vai reduzindo a quantidade de dinheiro que, de fato, você terá disponível durante o mês.

Assim também gambiarras são parte do desenvolvimento. Um time de desenvolvimento tem uma capacidade praticamente fixa, similar a um salário de uma pessoa. Mas, bem sabemos que não vivemos no mito do mundo sem prazos ou sem expectativas onde se pode levar um prazo infinito para se desenvolver. Então vai aqui a primeira lei de Thiago Mata - até que alguém prove que já disse isso antes: 

"Quando se demanda, de um time, uma produtividade acima da sua capacidade, alguém vai fazer uma gambiarra".

A gambiarra dá a impressão que seu time conseguiu produzir acima da capacidade assim como, o empréstimo, dá a impressão de que você conseguiu, em um mês, ganhar acima do seu salário e logo está mais rico. Mas isso, é uma ilusão. Você na verdade está mais pobre.

Manter uma gambiarra num projeto é manter uma dívida rolando sem pagar. Quanto mais tempo levar, mais complexo será alguém saber resolver, alguém entender como deveria ter sido feito e corrigir. Assim, quanto mais tempo levar, menos o seu time terá disponível para produzir outras coisas. Não resolver a gambiarra é como ignorar a prestação do cartão de crédito, ela não só não se resolve como piora a cada dia.

Existem dívidas tão grandes que são acima do seu salário mensal. Eu mesmo já precisei de uma assim. O que você faz para não perder o controle? Você cria um plano de pagamento. Todo mês, de alguma forma, seja carne ou débito em conta ou boleto, você reduz a sua dívida progressivamente, até quita-la totalmente.

Para sistemas com muitas gambiarras, é preciso fazer algo similar. Deve-se fazer um plano de melhoria do software, para que sua equipe, utilize um pouco da sua capacidade produtiva para resolver as gambiarras do passado, no lugar de produzir novas funcionalidades. Mas a equipe vai produzir menos? É claro! O empréstimo foi feito e agora está sendo pago, e com juros.

Todos nós temos um limite de crédito. Mesmo que eu queira pegar um dívida milionária, o banco não vai aceitar, porque ele sabe que eu não tenho como pagar. Assim, é preciso muitas vezes que uma equipe de desenvolvimento saiba o seu limite máximo de gambiarras o que, seguindo a analogia da lógica monetária, deve ser proporcional a sua capacidade produtiva.

Eu já convivi com projetos que, a cada dia, tinham uma capacidade produtiva menor. Para tentar reduzir esse problema, as empresas, aumentavam o tamanho do time. Mas, utilizaram de gambiarras para resolver gambiarras, que é como pegar empréstimo para quitar empréstimo, no juro sobre juro. Até chegar ao ponto que, mudar a cor de um botão, precisava de duas semana com tantos desenvolvedores. Projetos assim "faliram" ou estão a beira da "falência".  Uma empresa fali quando todo o seu patrimônio não é suficiente para pagar suas dívidas. Segundo Thiago Mata, até que alguém prove o contrário:
"Um projeto fali quando gerar um conjunto de novas funcionalidades neste demora, quase tanto ou mais do que fazer um novo com elas" 

É importante dizer que, essas coisas acontecem, não por falta de esforço ou capacidade produtiva das equipes, mas  falta de gestão estratégica de quem gerenciava esse pessoal.

Então, mantendo a dica mais fundamental de economia, procure não fazer empréstimos e gambiarras, planejando bem seus gastos e entregas. Caso não seja possível, procure fazer o uso com moderação, já fazendo um plano de abatimento destas, parcelado ou não. Deste modo você não tornará o seu time, a próxima Grécia da TI.

Ps - É importante ressaltar que estou chamando de gambiarra aqui, soluções que não são as mais rápidas ou mais recomendadas mas que geram um produto que apresenta os resultados esperados, sejam lógicos ou matemáticos. Entregar intencionalmente um produto que informa dados inválidos, que só parece que funciona, não é apenas uma gambiarra, é trapaça! pra lá de bandidagem. Nesse cenário é melhor jogar limpo com o cliente que não é possível entregar o produto no prazo do que entregar uma fraude.

sexta-feira, 9 de setembro de 2011

Resolvendo Problemas com Aspas Duplas do Zend_Db_Table_Abstract

Resumo,

Quando você tentar apontar o Zend_Db_Table_Abstract do Zend Framework com o Oracle, para gerar comandos SQL é possível que você enfrente o mesmo problema que nós enfrentamos, aspas duplas aparecem e fazem seus comandos SQL inválidos. Para resolver isso adicione  a seguinte linha:

resources.db.params.options.autoQuoteIdentifiers = 0

no seu application.ini e seja feliz.

Versão Longa,

Quando o Zend_Db_Table_Abstract gera erros ao tentar executar seus comandos SQL triviais, gerados automaticamente, qual é o 1º passo? Eu costumo gerar o assemble() do comando SQL para poder testa-lo diretamente no banco de dados. 

Como o problema que temos é com qualquer método, então testamos no mais simples, sobrescrevendo o getAll e fazendo isso:

public function getAll($where = null, $order = null, $count = null, $offset = null)
{
var_export( $this->select()->assemble() );
exit();
}

Então obtemos:

SELECT "tb_estado".* FROM "db_projeto"."tb_estado"

Sabemos agora qual é o problema: As consultas geradas automaticamente pelo Zend estão colocando aspas duplas no nome das tabelas e do schema, o que não é aceito no Oracle.

Após pesquisar um pouco achei esse link http://doczf.mikaelkael.fr/1.10/pt-br/zend.db.select.html que informa:

O método quoteIdentifier() usa aspas no SQL para delimitar o identificador, o que deixa claro que ele é um identificador de uma tabela ou coluna e não parte da síntaxe SQL.

Agora qual é o problema: As consultas geradas automaticamente pelo Zend estão colocando aspas duplas no nomes devido ao método _quoteIdentifier, que funciona do seguinte modo:

1009     /**
1010      * Quote an identifier.
1011      *
1012      * @param  string $value The identifier or expression.
1013      * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option.
1014      * @return string        The quoted identifier and alias.
1015      */
1016     
protected function _quoteIdentifier($value$auto=false)
1017     {
1018         if (
$auto === false || $this->_autoQuoteIdentifiers === true) {
1019             
$q $this->getQuoteIdentifierSymbol();
1020             return (
$q str_replace("$q""$q$q"$value) . $q);
1021         }
1022         return 
$value;
1023     } 

O nosso problema agora tem um foco bem mais específico. Como fazer o atributo protegido _autoQuoteIdentifiers ser falso. Logicamente se o atributo fosse público ou ao menos tivesse um setAutoQuoteIndentifiers ou algo similar, estaria agora resolvido o problema, na nossa classe pai que herda o Zend_Db_Table_Abstract, algo muito parecido com:

    public function init()
    {
$this->_db->setAutoQuoteIndentifiers( false ); 
    }

Mas, para nossa tristeza e agonia esse método setter não existe. Então precisamos descobrir como esse atributo recebe um valor.

Fazendo uma busca na classe, vemos que esse atributo pode ter seu valor atribuído caso ele venha no Array que é recebido no método construtor da classe 

238 
239         
// obtain quoting property if there is one
240         
if (array_key_exists(Zend_Db::AUTO_QUOTE_IDENTIFIERS$options)) {
241             
$this->_autoQuoteIdentifiers = (bool) $options[Zend_Db::AUTO_QUOTE_IDENTIFIERS];
242         }
243 

Tentando entender como essa variável $options é montada, vemos que essa variável é um Array que é inicializado com os valores padrões:

185         $options = array(
186             
Zend_Db::CASE_FOLDING           => $this->_caseFolding,
187             
Zend_Db::AUTO_QUOTE_IDENTIFIERS => $this->_autoQuoteIdentifiers
188         
);

Mas que tem esses valores alterados conforme os elementos que são recebidos no campo "options" do $config. Sendo $config o Array que é recebido no construtor.

190 
191         
/*
192          * normalize the config and merge it with the defaults
193          */
194         
if (array_key_exists('options'$config)) {
195             
// can't use array_merge() because keys might be integers
196             
foreach ((array) $config['options'] as $key => $value) {
197                 
$options[$key] = $value;
198             }
199         }

Procurando então em que local do Zend_Db_Table_Abstract que é instanciado o atributo $this->_db
Chegamos nas seguintes linhas:

575     
/**
576      * @param  mixed $db Either an Adapter object, or a string naming a Registry key
577      * @return Zend_Db_Table_Abstract Provides a fluent interface
578      */
579     
protected function _setAdapter($db)
580     {
581         
$this->_db self::_setupAdapter($db);
582         return 
$this;
583     } 

Seguindo o rastro, vamos ao método _setupAdapter:

594 
595     
/**
596      * @param  mixed $db Either an Adapter object, or a string naming a Registry key
597      * @return Zend_Db_Adapter_Abstract
598      * @throws Zend_Db_Table_Exception
599      */
600     
protected static function _setupAdapter($db)
601     {
602         if (
$db === null) {
603             return 
null;
604         }
605         if (
is_string($db)) {
606             require_once 
'Zend/Registry.php';
607             
$db Zend_Registry::get($db);
608         }
609         if (!
$db instanceof Zend_Db_Adapter_Abstract) {
610             require_once 
'Zend/Db/Table/Exception.php';
611             throw new 
Zend_Db_Table_Exception('Argument must be of type Zend_Db_Adapter_Abstract, or a Registry key where a Zend_Db_Adapter_Abstract object is stored');
612         }
613         return 
$db;
614     } 

Vemos então que esse método consulta o Zend_Registry, o que já era esperado. Sem querer entrar na complexidade desse Registry, nós sabemos que o Zend_Registry do Banco de Dados é informado no application.ini. É lá que informamos dados como login, senha, nome do banco de dados, etc. Então, será que os dados que o construtor do Zend_Db_Table_Abstract apenas lê os dados vindos do application.ini, na parte dos "resources.db"? 

Para testar essa hipótese eu vou imprir o $config do construtor do Zend_Db_Table_Abstract. Como qualquer alteração feita no código do Zend Framework isso é apenas para auxiliar o nosso entendimento e deverá ser desfeita em breve.

Ao adicionar no construtor a seguinte linha:

    public function __construct($config)
    {
var_export( $config );
exit();
// (...)

Obtemos o seguinte resultado:

array (
  'host' => 'banco_do_projeto',
  'username' => 'usuario_do_servidor',
  'password' => '******',
  'dbname' => 'db_projeto',
  'charset' => 'utf8',
  'profiler' => 
  array (
    'enabled' => '1',
  ),
)

Precisamos adicionar uma linha no nosso application.ini para testar se essa chegará até o $config. Mas qual seria o valor dessa linha. Por semelhança aos demais elementos que chegaram, deve ser algo similar a resources.db.params.options.xxxxxxxxxxx = 0. Mas, para encontrarmos o valor que desejamos, precisamos saber qual é o valor da chave. Esse valor nós podemos ver aqui:

185         $options = array(
186             
Zend_Db::CASE_FOLDING           => $this->_caseFolding,
187             
Zend_Db::AUTO_QUOTE_IDENTIFIERS => $this->_autoQuoteIdentifiers
188         
); 


Usando o CRTL+Click da IDE, já fui direto para a declaração desta constante, conforme:

const AUTO_QUOTE_IDENTIFIERS 'autoQuoteIdentifiers'

Logo a nossa nova linha no application.ini, deve ser:

resources.db.params.options.autoQuoteIdentifiers = 0

Obtemos o seguinte resultado:

array (
  'host' => 'banco_do_projeto',
  'username' => 'usuario_do_servidor',
  'password' => '******',
  'dbname' => 'db_projeto',
  'charset' => 'utf8',
  'options' => 
  array (
    'autoQuoteIdentifiers' => '1',
  ),
  'profiler' => 
  array (
    'enabled' => '1',
  ),
)

Esse novo elemento no $config vai alterar o atributo protegido _autoQuoteIdentifiers retirando as aspas dos SQL gerados e resolvendo o nosso problema.

Espero que isso tenha ajudado.

Um grande abraço.

segunda-feira, 16 de maio de 2011

O Template do InepZend

Ainda sem nome próprio, o template do Inep Zend Framework tem sido uma das partes mais desafiadoras e excitantes de todo o projeto. Por isso resolvi começar falando logo deste componente.

Provavelmente, você deve estar com alguma(s) das seguintes indagações abaixo:
  • PHP precisa de Template?
  • Smarty já não é bom o bastante?
  • Será que vou ter de aprender mais uma linguagem de Template?
PHP precisa de Template?

É possível desenvolver bons projetos, de alta complexidade em PHP sem utilizar templates? Sim. Tanto que o Zend Framework vem nativamente sem nenhuma camada de Template. Mas então, qual é a vantagem do Template? Em linhas gerais, ao se manter a complexidade lógica da aplicação separada do controle da apresentação se reduz o risco de que mudanças no layout gerem impacto sob a lógica da aplicação e vice-versa. A vantagem da camada de Template é bem similar a vantagem de se dividir o código em camadas. Se reduz o acoplamento sem perder a coesão. Com o uso de Templates, o impacto de alterações se torna menor e se facilita identificar o local responsável por um erro. Os pontos negativos também são similares a divisão de camadas, se tem um aumento de complexidade do projeto e um provável aumento no tempo de processamento.

Se a sua empresa tem a divisão entre programadores e designers é quase certo que você precisa de fazer uso de uma camada de Template ( mesmo que não esteja fazendo isso ). Se o seu projeto faz uso de mascaras nos elementos de entrada, tais como mascaras para entradas de valores em dinheiro, entradas de datas, etc., pense seriamente no impacto de alterar essa sua mascara para funcionar num navegador novo, por exemplo.

Um outro problema freqüente é que os programadores não se preocupam tanto com a qualidade do HTML gerado. Basta tentar validar o seu projeto com alguma das ferramentas disponíveis, tais como o http://validator.w3.org/ para perceber isso. É muito comum que o HTML válido que o designer passa para a equipe de programação, é alterado várias vezes pela equipe de programação, até que passa a apresentar vários erros de HTML, que são simplesmente ignorados pelos programadores. A complexidade de algumas solução de Template, dificultam ainda mais o controle sobre o HTML desenvolvido. Uma solução clássica de cabeçalho e rodapé gera um problema muitas vezes difícil de resolver. Não será intuitivo, para quem for dar manutenção nos seus arquivos, saber qual é, de todos os arquivos que contém HTML, aquele que fecha a tag da tabela que é aberta no arquivo atual. 

Finalmente uma camada de Template pode facilitar a utilização de soluções complexas de modo mais transparente e padronizado. Por exemplo, transformar um formulário de submissão tradicional POST em Ajax pode ser automatizado pela ferramenta desde que informado algum atributo específico. Evita-se assim a criação de várias soluções diferentes para o mesmo problema que além de serem um problema para a manutenção do projeto é um desperdício do tempo de desenvolvimento do programador. Mais argumentos sobre isso você pode encontrar aqui.

Smarty já não é bom o bastante?

Entre as soluções disponíveis de Templates para PHP, o Smarty certamente é a mais popular. O Dwoo intenta ser uma versão um pouco mais moderna do Smarty, mas mantendo o máximo possível de compatibilidade e mantém também vários dos problemas descritos abaixo. As outras soluções encontradas, tais como o Sigma, Twig, Haanga também apresentam alguns dos problemas citados no caso do Smarty.

Os principais motivos que nos levou a decidir não utilizar o Smarty foram:
  • Não disponibilizar validação do HTML gerado;
Para o Smarty, assim como no PHP nativo, o resultado gerado é um texto. Se o texto gerado é um HTML ou não, é um controle que é feito apenas pelo programador. Deste modo, caso o HTML gerado não seja válido, o Smarty não irá fazer nenhum tipo de alerta a respeito, deixando o resultado inválido ser entregue ao usuário.
  • Não ser XML;
Praticamente qualquer IDE é capaz de fazer uma validação de XMLs. O padrão XML é conhecido tanto por designers quanto por programadores e é o formato padrão em que o XHTML é escrito. Nada faz mais sentido de que o próprio Template seja também em XML.
  • Não ser flexível para novas funcionalidades;
O Smarty, segundo a avaliação da nossa equipe, se mostrou pouco flexível para a criação de novas funcionalidades. Chegamos até a editar algumas das expressões regulares do Smarty para tentar deixar o comportamento dele mais flexível antes de decidirmos partir para uma solução própria.
  • Não ser orientado a objetos.
Por ter sido desenvolvido para PHP4. O Smarty não faz uso dos recursos de orientação a objetos do PHP5. Um componente de template que faça uso de orientação a objetos viabiliza uma personalização e compreensão mais fácil do código em execução, mais flexibilidade na geração de tags personalizadas e ainda a geração e alteração de telas por métodos DOM

Será que vou ter de aprender mais uma linguagem de Template?

Agora que espero que você já esteja convencido de que precisa de algum Template mais moderno que o Smarty, você deve estar sofrendo com o problema de ter de aprender uma nova gramática de Template. Essa preocupação é perfeitamente compreensível, pois cada ferramenta de Template cria uma versão tão personalizada "para melhor" das tags do HTML. Então, passar a utiliza-la é quase como aprender um HTML diferente. As confusões são inevitáveis assim como o ódio das alterações de nome sem sentido.

Uma das metas nessa nossa nova ferramenta de Template foi, no máximo possível, não criar tags novas. Então todas as tags do HTML5 funcionam exatamente como são no HTML5. Exatamente isso, escreva um HTML válido e ele já é um Template válido. Se você escrever um HTML inválido o PHP irá gerar uma Exception informando qual regra do HTML5 foi quebrada. Isso mesmo, o PHP através do Template faz validação Server Side do HTML gerado pelo Template. Na versão atual da ferramenta essa validação é obrigatória mas devemos deixa-la opcional para que possa deixar o sistema mais rápido em produção, etc.

Mas a vantagem de uma ferramenta de Template é exatamente seu dinamismo então, foi inevitável se criar as tags de repetição, de condição, etc. Então, foram sim criadas tags de template que provavelmente você terá que aprender. A vantagem é que todas essas novas tags são Classes PHP bem documentadas e com exemplos. Conforme pode ser visto aqui.

Na verdade todas as tags do Template são classes PHP. Inclusive as tags do HTML5, tornando assim possível a validação do HTML conforme citado acima. Transformamos cada Template XML num arquivo PHP, usando o componente Xml2Php, fazendo cache, usando o componente de Cache, que monta uma arvore de objetos que, quando desenhada, monta uma página XHTML.

Então temos aqui todas as tags do HTML5 escritas no formato de classes bem documentadas. Mesmo que você não tenha se convencido da idéia dos Templates ou não tenha gostado da nossa solução, pode ser útil como ferramenta de pesquisa.

Todas as tags podem ser estendidas, podendo você ter tags específicas do seu projeto. Por exemplo essa tag Numero estende a tag Input colocando uma mascara Javascript.

Então, resumindo tudo num exemplo: esse template, vira esse arquivo php que quando interpretado é validado e monta dinamicamente uma página XHTML5.

Espero que tenham gostado do nosso Template. Em breve devemos disponibilizar tutoriais para explicar melhor item a item dentro do site do projeto. Esse post foi mais um making-off das motivações e decisões que levaram o projeto até onde ele está.

Abraços,
Thiago Mata.






Algumas perguntas e repostas:

Rafael Barbosa disse...



Sem querer desmerecer o projeto, mas pelas motivações e implicações colocadas eu não entendi porque não utilizar um template engine pronto. O Twig, por exemplo, tem tudo que você precisa e já está pronto.

Sem contar que transformar markup em objetos pra transformar em markup de novo simplesmente pra garantir que esteja bem formado me parece MUITO overkill.

Eu trabalhei muito tempo no governo e sei que é difícil conseguir agregar um ferramental novo ao trabalho, mas ainda acho que vale o esforço de tentar utilizar algo que já está estável e é amplamente aceito pela comunidade do que construir algo do zero.


Thiago Mata responde...


Primeiramente seu comentário é muito pertinente. A resposta que eu vou lhe passar devo colocar na própria documentação do projeto mas talvez ainda assim faça um adendo no post falando disso.

Conforme você já deve ter notado, a solução que fizemos foi muito similar ao Jsf http://exadel.com/web/portal/jsftags-guide, Seam, ColdFusion, JLibs http://code.google.com/p/jlibs/wiki/XMLDocument,  então vou evitar ser repetitivo e dizer que os pontos positivos e negativos dessas soluções são bem próximos. Basta uma busca sobre Jsf ou Jlibs e você verá pessoas defendendo e apoiando conforme o contexto. Apenas ressaltando que na nossa implementação, foram mantidas os nomes e os atributos das tags do html.

Apesar do custo das conversões Xml => OO => Xml ser de fato um custo grande, a diferença em termos de tempo resposta, mesmo quando fizemos simulações de vários milhares de acessos simultâneos, não foi tão grande ao ponto de ser considerado impeditivo, tendo em vista o porte das máquinas que temos. Mas compreendo que isso pode não ser o caso em outras empresas.

Utilizar esse recurso de Xml => OO => Xml nos permite muito mais do que apenas validar o Xhtml, esse foi um dos argumentos do post que já estava muito extenso.  Esse argumento se mostrou importante aqui na empresa por que foi uma das reclamações que nossos usuários finais fazem dos nossos sistemas. Como estamos ainda tendo que trabalhar com fabricas de softwares, controlar a qualidade do xhtml gerado é essencial. Lembre-se que temos sistemas em que os usuários são deficientes visuais ou com inabilidades motoras onde uma página html mal feita pode ser impeditivo ao uso.

Dentre outras vantagens dessa solução temos o encapsulamento de componentes complexos de tela em objetos. Podemos ter as tags personalizadas como etc. E, a mesma validação que funciona nas tags do xhtml, irá validar se os campos de cada uma dessas tags foram adequadamente preenchidos, assim como já está funcionando com as tags de template. A criação dessas tags personalizadas pode ser feita utilizando outros templates combinados com comandos DOM, ou fazendo uso apenas de objetos e DOM, etc. Tudo isso utilizando as vantagens de uma organização OO. Essa capacidade de encapsulamento de complexidade e padronização de arquitetura de modo intuitivo no template foi uma das melhorias solicitadas para a nossa equipe.

Se você avaliar o exemplo da tag personalizada Number que utilizei no post, irá notar que uma tag no meio do conteúdo adicionou um script no head. Então, conforme pode ser visto nesse exemplo, as tags podem interagir entre si, adicionando ou alterando o comportamento de tags anteriores. Com isso nós podemos fazer, por exemplo, páginas que façam carga apenas dos arquivos javascript e css que forem ser utilizados. Podemos também fazer facilmente que uma tag especial seja a soma dos valores de outras tags, a média dos valores, etc. sem exigir conhecimento de javascript.

Uma das idéias das próximas é viabilizar a "adequação" do html de resultado para o navegador do cliente. O programador / designer continuará escrevendo HTML5 e será de responsabilidade do framework fazer as "correções" necessárias para funcionar nos browsers não modernos.

Por último, tendo em vista que temos uma versão server side da página do usuário, podemos armazena-la na sessão, e então fazer uma sincronia das alterações usando ajax. Em outras palavras, podemos fazer manipulações server side no objeto tela, a partir de eventos de tela enviados ao servidor, que depois é sincronizado no cliente via ajax. Esse passo ainda está em iniciação no Inep Zend mas no Coruja Php Framework já conseguimos isso com sucesso. Chegamos a desenvolver uma planilha similar ao Excel / Calc utilizando esta técnica com toda lógica de negócio necessária para a interpretação das fórmulas e alteração em cascata do valor dos campos no Server Side.

Além de tudo, não obrigamos nossa equipe a utilizar esse template o tempo todo. Conforme foi dito mantemos a compatibilidade do tudo que já funcionava no Zend, inclusive o phtml. Então, caso numa página ou layout o uso do template se mostre desnecessário ou inadequado, o programador pode fazer uso da solução padrão do Zend. Pode-se até pensar de maneira invertida, utilizando sempre phtml e ter essa ferramenta de template caso se deseje fazer alguma página de maior complexidade.

Espero ter esclarecido,
Thiago Mata.




Rafael Barbosa disse...


Caro Thiago,

posso postar no seu blog esse comentário depois sem problema.

Eu entendo o embasamento que você teve de outras ferramentas, e acho válida a iniciativa de querer melhorar as ferramentas de trabalho. O que me preocupa é o tempo gasto com algo que, a meu ver, tem muito mais desvantagens do que vantagens.

A parte da validação de markup não deveria ser responsabilidade da framework e pode ser remediada de N formas. A mais simples delas é exigir a validação das páginas com as ferramentas da w3c na rotina de testes/homologação do seu sistema. Isso é muito simples de justificar e eu julgo necessário mesmo tendo a validação interna no pré-processamento da requisição. Logo, esse problema de validação tem uma solução que não necessita escrever 1 linha de código.

O custo da transformação do markup para objeto se torna linear se vc usar algum ferramenta (por mais simples que seja) de cache. Eu não disse que o custo era ALTO e sim DESNECESSÁRIO (usei o termo OVERKILL). O que você vai ter com isso é mais um ponto de falha, mais um módulo para dar manutenção e mais um pedaço da sua arquitetura para documentar e testar sem ter o ganho que isso deveria te proporcionar.

Quanto às tags personalizadas elas já existiam no Smarty (apesar de ser um inferno extender aquilo) e existem no Twig (que na minha humilde opinião é a melhor engine de template que existe) e em outras ferramentas. Talvez o que exista no seu caso seja uma preferência em usar tags fechadas por <> (o que eu não acho legal, mas entendo ser outra questão), nesse caso basta alterar uma dessas bibliotecas para utilizar as tags como você gosta, assim você não precisa reinventar a roda.

Guardar a página do usuário em sessão e atualizar via ajax. Isso realmente é algo que você deveria repensar antes de implementar. Alguns motivos para isso:
1 - Você já tem todo o DOM bonitinho na tela do seu usuário ( em JavaScript). Ter isso em sessão é simplesmente duplicar uma informação, uma vez que o servidor não pode fazer muita coisa com ela.
2 - Tudo que existir na tela (client) vai ter que de alguma forma sincronizar de volta com o servidor. Isso é completamente desnecessário, visto que o browser já armazena o estado da tela e em último caso você pode armazenar isso em um cookie via javascript e poupar uma requisição.
3 - Quando você mandar do servidor de volta todo o markup pra tela do cliente você vai precisar carregar de novo todos os binds de objetos no javascript (inclusive os que não mudaram), o browser vai ter que renderizar de novo a página toda. (O cache ajuda, mas não vai fazer milagre)
4 e mais importante: Se você, de alguma forma, for atualizar pedaços da interface com a resposta do servidor em vez de atualizá-la por completo, qualquer framework de javascript faz esse trabalho de forma stateless/rápida e ainda te permite alterar o history do browser com o pushState sem quebrar o botão de voltar.

A parte de corrigir o markup de acordo com a versão do browser faz sentido, mas dificilmente você vai esbarrar com uma situação em que alterar o html resolva uma incompatibilidade que você não consiga resolver com alguma camada de abstração pronta em javascript/css.

Mais uma vez, eu aprecio a iniciativa e pelo nível de complexidade de uma ferramenta dessas eu acredito que você e os membros da sua equipe sejam ótimos programadores, mas acho que a força de trabalho de vocês deveria ser utilizada pra construir algo novo e melhor e não ficar reescrevendo ferramentas que já existem.

Dá uma olhada do que tem sido feito nos projetos da sensio-labs. Especialmente o Symfony 2 e o Doctrine 2. Eles estão escrevendo as bibliotecas mais avançadas em php 5.3 que existem. Vale a pena dar uma olhada e tentar participar dessa evolução da das ferramentas e da linguagem junto com a comunidade.

Atenciosamente,
Rafael Barbosa





Thiago Mata responde...

Gostei dos seus comentários como sempre,

Eu entendo o embasamento que você teve de outras ferramentas, e acho válida a iniciativa de querer melhorar as ferramentas de trabalho. O que me preocupa é o tempo gasto com algo que, a meu ver, tem muito mais desvantagens do que vantagens.

Ainda não vi tantas desvantagens. Replicar as tags do HTML foi um trabalho massante, de fato, mas feito com baixa complexidade e em relativamente pouco tempo. Bastou lermos a especificação do Mozilla http://developer.mozilla.org/en/HTML/Element/  e  como eu te falei não tem tanto impacto na performance. A complexidade do código não é tão grande e pretendemos fazer bons testes com ele com PHPUnit o que deve reduzir consideravelmente a chance de algum erro. Então não vi as desvantagens. 

A parte da validação de markup não deveria ser responsabilidade da framework e pode ser remediada de N formas. A mais simples delas é exigir a validação das páginas com as ferramentas da w3c na rotina de testes/homologação do seu sistema. Isso é muito simples de justificar e eu julgo necessário mesmo tendo a validação interna no pré-processamento da requisição. Logo, esse problema de validação tem uma solução que não necessita escrever 1 linha de código.

Estou certo que não preciso te lembrar que todas as páginas são dinâmicas e testar todas as combinações pode ser um trabalho sem fim. Garantir continuamente a qualidade do HTML é um ponto positivo. O teste de validação vai continuar existindo mas será feito sempre em algumas poucas das saídas possíveis. Além do que as mensagens de erro do Framework podem ser mais claras do que a validação externa como mostrar em qual arquivo de Template o erro aconteceu. Lembrando que essa validação de atributos obrigatórios, valores default, etc. deve existir para as tags personalizadas então estamos reaproveitando esse recurso para validar também as tags do HTML.

Quanto às tags personalizadas elas já existiam no Smarty (apesar de ser um inferno extender aquilo) 

INFERNO!!!

e existem no Twig (que na minha humilde opinião é a melhor engine de template que existe) e em outras ferramentas. Talvez o que exista no seu caso seja uma preferência em usar tags fechadas por <> (o que eu não acho legal, mas entendo ser outra questão), nesse caso basta alterar uma dessas bibliotecas para utilizar as tags como você gosta, assim você não precisa reinventar a roda.

Até onde eu sei. O Twig ainda segue uma linha de templates de arquivos incluindo arquivos que já vi várias vezes fugindo de controle. Mas prometo que vou avalia-lo mais a fundo. Se ele se mostrar uma solução que venha a atender nossas necessidades nós poderemos passar a utiliza-lo ou deixa-lo como uma opção de template alternativa.

Guardar a página do usuário em sessão e atualizar via ajax. Isso realmente é algo que você deveria repensar antes de implementar. Alguns motivos para isso:
1 - Você já tem todo o DOM bonitinho na tela do seu usuário ( em JavaScript). Ter isso em sessão é simplesmente duplicar uma informação, uma vez que o servidor não pode fazer muita coisa com ela.
2 - Tudo que existir na tela (client) vai ter que de alguma forma sincronizar de volta com o servidor. Isso é completamente desnecessário, visto que o browser já armazena o estado da tela e em último caso você pode armazenar isso em um cookie via javascript e poupar uma requisição.
3 - Quando você mandar do servidor de volta todo o markup pra tela do cliente você vai precisar carregar de novo todos os binds de objetos no javascript (inclusive os que não mudaram), o browser vai ter que renderizar de novo a página toda. (O cache ajuda, mas não vai fazer milagre)
4 e mais importante: Se você, de alguma forma, for atualizar pedaços da interface com a resposta do servidor em vez de atualizá-la por completo, qualquer framework de javascript faz esse trabalho de forma stateless/rápida e ainda te permite alterar o history do browser com o pushState sem quebrar o botão de voltar.

1. Ter 100kb a mais para cada sessao de cada usuário logado não é exatamente um problema terrível. Lembre-se que essa página já existe como arvore de objetos no servidor então eu só serializo e salvo na sessão. No ambiente de produção muitas vezes os "arquivos" da sessão ficam numa area da memória RAM. Então fica muito realmente rápido. Podemos também guardar esse dado serializado em outros lugares além da sessão como no banco ou num arquivo separado, etc.

2. Não entendi. Tenho a impressão que você não entendeu exatamente como nós fizemos essa sincronia. As operações nesse contexto vão acontecer no servidor. Veja GWT, que é algo muito similar a isso.

3. Não vamos mandar de volta uma versão toda nova da tela. Apenas receber um json com o que mudou em quais tags. Novamente recomendo você dar uma boa olhada no GWT.

4. Pretendo sim utilizar algum framework Javascript para fazer essa comunicação com o servidor. Mas isso não impede de precisamos de um servidor capaz de receber o evento e saber quais tags devem ser alteradas e quais devem ser seus novos conteúdos etc.

Thiago Mata.




Rafael Barbosa disse...

Caro Thiago,

Ainda não vi tantas desvantagens. Replicar as tags do HTML foi um trabalho massante, de fato, mas feito com baixa complexidade e em relativamente pouco tempo. Bastou lermos a especificação do Mozilla
http://developer.mozilla.org/en/HTML/Element/  e  como eu te falei não tem tanto impacto na performance. A complexidade do código não é tão grande e pretendemos fazer bons testes com ele com PHPUnit o que deve reduzir consideravelmente a chance de algum erro. Então não vi as desvantagens. 

Ter testes unitários vai mesmo reduzir bugs e te ajudar a dar manutenção, o que não quer dizer que essa manutenção deixe de existir. Como eu disse, como qualquer parte do sistema, tem que ter uma razão pra estar lá senão não vale o esforço de manutenção.


Estou certo que não preciso te lembrar que todas as páginas são dinâmicas e testar todas as combinações pode ser um trabalho sem fim. Garantir continuamente a qualidade do HTML é um ponto positivo. O teste de validação vai continuar existindo mas será feito sempre em algumas poucas das saídas possíveis. Além do que as mensagens de erro do Framework podem ser mais claras do que a validação externa como mostrar em qual arquivo de Template o erro aconteceu. Lembrando que essa validação de atributos obrigatórios, valores default, etc. deve existir para as tags personalizadas então estamos reaproveitando esse recurso para validar também as tags do HTML.

Manter o HTML válido sempre é interessante, mas eu acho que isso deveria ser uma tarefa executada ao empacotar sua aplicação e não a cada request. Uma solução mais limpa (e que não exigiria a recriação das views da sua aplicação) seria a criação de testes de aceitação pra cada uma das funcionalidades onde, além de executar o teste de aceitação da especificação você utilizasse os webservices da W3C para validar o markup gerado ao final do teste. Dá um pouco de trabalho adaptar o PHPUnit pra fazer esse tipo de teste mas o tempo que isso vai te poupar a longo prazo é absurdo!

Até onde eu sei. O Twig ainda segue uma linha de templates de arquivos incluindo arquivos que já vi várias vezes fugindo de controle. Mas prometo que vou avalia-lo mais a fundo. Se ele se mostrar uma solução que venha a atender nossas necessidades nós poderemos passar a utiliza-lo ou deixa-lo como uma opção de template alternativa.
Como todo código extensível (e isso inclui o próprio php) se você não definir um padrão de uso os arquivos vão fugir do controle. Como exemplo o ministério da saúde migrou de phtml para twig. Os arquivos em si não mudaram, só o conteúdo, o twig não exige nenhuma extensão específica. Dessa forma a sanidade em relação aos arquivos foi mantida.

1. Ter 100kb a mais para cada sessao de cada usuário logado não é exatamente um problema terrível. Lembre-se que essa página já existe como arvore de objetos no servidor então eu só serializo e salvo na sessão. No ambiente de produção muitas vezes os "arquivos" da sessão ficam numa area da memória RAM. Então fica muito realmente rápido. Podemos também guardar esse dado serializado em outros lugares além da sessão como no banco ou num arquivo separado, etc.

2. Não entendi. Tenho a impressão que você não entendeu exatamente como nós fizemos essa sincronia. As operações nesse contexto vão acontecer no servidor. Veja GWT, que é algo muito similar a isso.

3. Não vamos mandar de volta uma versão toda nova da tela. Apenas receber um json com o que mudou em quais tags. Novamente recomendo você dar uma boa olhada no GWT.

4. Pretendo sim utilizar algum framework Javascript para fazer essa comunicação com o servidor. Mas isso não impede de precisamos de um servidor capaz de receber o evento e saber quais tags devem ser alteradas e quais devem ser seus novos conteúdos etc.

1 - Não é um problema terrível se você precisar mesmo disso. Eu ainda não entendi a vantagem de se passar a lógica do cliente para o servidor.

2 e 3 - Realmente eu tinha entendido diferente. Dessa forma realmente faz sentido.

4 - Nesse caso depende muito do que você vai precisar fazer. Eu acho que apesar de não existirem grandes problemas, a priori, de manter a página em sessão acredito que para boa parte dos casos não existe essa necessidade. Movimentar coisas na tela por exemplo não precisaria chegar no servidor. Pode ser que para o tipo de aplicação que você desenvolva aí isso faça sentido e eu esteja falando besteira =)


Atenciosamente,
Rafael Barbosa




Thiago Mata responde...

Gostei dos seus comentários como sempre,

Acredito que você tenha entendido muito bem a solução. Sei que ela apresenta uma complexidade relativamente alta que pode em alguns casos não compensar o uso. Mas conforme você mesmo citou existem casos e casos. Não digo que essa solução precisa / deva ser utilizada em qualquer caso. Na verdade, não acredito que nenhuma solução deva ser utilizada em qualquer caso, num complexo de "bala de prata". 

Mas conforme os exemplos já citados de GWT, JSF, JLIBS, SEAM, etc. existem contextos onde fazer essa conversão se justifica. Existem ainda cenários que utilizar essa solução não é necessário mas pode parecer interessante. Com o framework maduro e já bem otimizado, utilizar essa solução em contextos mais simples pode ser uma boa opção, pensando-se nos ganhos já citados. Novos pontos positivos serão criados a cada dia conforme enriquecemos a solução. Certamente terá pontos negativos, como qualquer decisão. Talvez quando eu tiver exemplos da interação entre os arquivos de template sem os muitos includes, fique mais claro outros ganhos que a utilização desse tipo de solução venha a apresentar. A solução em SEAM, com componentes personalizados, foi muito bem aceita pela equipes, por exemplo, aumentou a produtividade, a padronização e a qualidade dos softwares desenvolvidos. Estamos tentando aprender com as soluções que deram mais certo e errado dentro do nosso cenário e gerar soluções adequadas ao mesmo.

Resumindo, o que estamos oferecendo aqui é uma opção de solução que temos desenvolvido para se adequar à nossa demanda e ao nosso cenário. Acreditamos que ela pode vir a ser útil em outros cenários também. Certamente ela não será a melhor solução em qualquer cenário mas talvez venha a ser uma boa opção para um cenário que vocês venham a encontrar.

Atenciosamente,
Thiago Mata.