Archive

O código não interessa

Você já notou como tornar seu código bonito e elegante não é tão difícil uma vez que você sabe exatamente o que ele tem que fazer? Quando o problema está bem definido, não há muita dificuldade em tornar a solução mais elegante. Veja o problema da ordenação, por exemplo. Ao longo dos anos surgiram várias soluções justamente porque o problema é suficientemente simples e bem compreendido. Mas se você quiser se aventurar no reino das aplicações-corporativas-de-intranet-nossas-de-cada-dia, é melhor se preparar para ver um bocado de código feio.

Horrível, na verdade.

Esse tipo de código não é feio porque quem o escreve não saiba programar direito. Os programadores que fazem essas aplicações não podem deixar o código bonito porque na maioria dos casos eles não sabem muito bem o que construir. Para escrever código bonito, eles precisam primeiro saber o que o código deve fazer. Claro que também precisam estudar bastante e dominar as técnicas de programação mais recentes, mas não vão ter a mínima chance se não souberem o que estão tentando construir.

Entender bem os requisitos e fazer o que o cliente precisa é insanamente mais importante do que escrever código bonito (não que a beleza do código também não seja importante). É o trabalho que realmente importa em desenvolvimento de software, certo?

Então vamos todos parar de escrever código, ora bolas! Os requisitos é que interessam!

Nosso trabalho todo agora vai ser entender direito as necessidades do cliente. Há várias técnicas e ferramentas para fazer isso. Você pode tentar entender os requisitos usando diagramas, documentos de casos de uso e até um poster de papelão colado em uma parede na sala de desenvolvimento.

Ou então você pode usar — veja só — código.

Não precisamos separar as atividades de escrever código e analisar requisitos. A escrita do código pode muito bem fazer parte da análise de requisitos, assim como o tempo que se gasta escrevendo casos de uso ou pintando aquele poster lá.

Se o software está minimamente “pronto”, os clientes podem experimentá-lo, vê-lo funcionando e solicitar modificações quando necessário. Eles não podem fazer isso com casos de uso. Eles não conseguem ver os casos de uso funcionando até que alguém os transforme em código. Pode ser até pior: eles podem ver um diagrama (que também não funciona) e imaginar que vai funcionar de um jeito diferente do que a equipe de desenvolvimento imagina.

O famigerado código pode ser usado como uma ótima ferramenta para análise de requisitos. Nós somos programadores, não arquitetos. Não precisamos esperar os pedreiros levantarem o prédio para ver como vai ficar. No lugar dos pedreiros temos compiladores e interpretadores, não seres humanos. Arquitetos não podem se dar ao luxo de construir o prédio errado (bem que gostariam de poder fazer isso), por isso precisam investir bastante tempo nas especificações para ter certeza de que tudo vai funcionar como desejado. Nós não precisamos. Para um programador, derrubar o prédio e construir tudo novamente é tão simples quanto `make clean && make`. Ao invés de nos debruçar sobre especificações mortas, podemos observar como a coisa real se comporta. A coisa é chamada de software justamente porque é fácil (e barata) de ser modificada. Não tirar proveito da maleabilidade da nossa mídia é perda de tempo.

Então a lógica fica mais ou menos assim: o código não interessa porque o trabalho mais valioso é o de acertar os requisitos. Acertar os requisitos é descobrir o que o cliente precisa. Fica muito mais fácil descobrir o que o cliente precisa quando você tem software funcionando para mostrar. Software funcionando precisa de código. Logo, o código interessa.

Bom, na verdade o código não interessa tanto assim, mas precisamos desejar muita sorte para quem tentar entender os requisitos sem código.

Você pode ser ruim de mil maneiras

Corre um boato novo dizendo que Rails é o novo ASP, mas isso não é exclusividade de Rails (nem de ASP): qualquer coisa pode ser o novo ASP, basta apresentá-la às pessoas erradas. Programadores ruins serão ruins usando qualquer tecnologia. Tecnologicamente falando, não há nada que se possa fazer para torná-los menos ruins. Não há como projetar uma tecnologia que os impeça de ser ruins. Não é com novas linguagens, bibliotecas ou frameworks que eles vão melhorar. Algo que talvez ajude são idéias novas, mas eles precisam estar abertos a elas. No fim das contas a transformação depende somente deles mesmos. Parece auto-ajuda mas é bem verdade.

Há basicamente duas maneiras de novas tecnologias influenciarem programadores ruins a escreverem software menos ruim. A primeira é tornar as boas práticas extremamente fáceis de modo que simplesmente pareçam o jeito natural de fazer as coisas. A segunda é tomar o caminho contrário e tornar práticas ruins mais difíceis ou feias de se fazer. Pode-se usar algo como vinagre sintático para fazer isso. O problema de tornar práticas não recomendadas mais feias é que os programadores ruins vão escrever o código feio e ignorar completamente a feiúra. Os olhos deles são incapazes de enxergar feiúra em um trecho de código. Se você quiser realmente seguir por este caminho, precisa dar um jeito de proibir por completo as asneiras mais gritantes.

Mas só é possível enxergar as mais gritantes.

Mesmo que você consiga evitar os piores erros, surgirão novas idéias igualmente burras. Você pode tentar bloquear essas também e continuar tentando proteger as pessoas delas mesmas até um certo ponto. Mas infelizmente não é possível proibir um idiota de estragar tudo. Os idiotas precisam estragar tudo. É a natureza deles. É isso que os define. É por isso que eles são chamados de idiotas. Não importa quantas bolas de chumbo você amarre aos pés deles, os verdadeiros idiotas vão arrumar um jeito de fazer besteira. Se sua tecnologia resolver seguir por este caminho, vai precisar proibir tanta coisa e ficar tão limitada que só os tapados serão atraídos. Aqueles que sabem o que estão fazendo simplesmente não aceitam trabalhar acorrentados.

Já que o caminho da proibição não é tão promissor, é melhor explorar a alternativa: tornar o “certo” idioticamente fácil. Desse modo, os bons profissionais serão atraídos pela elegância das soluções e vão começar a falar sobre a sua tecnologia. Com o burburinho todo mundo vai querer dar uma olhada nesse novo negócio de que os caras da moda andam falando tanto. As idéias ruins serão contidas por algum tempo, mas há um efeito indesejável que você não vai conseguir evitar.

Este efeito está intimamente ligado à maldição da popularidade. Mais popularidade significa mais gente e mais gente significa mais gente tapada. Sua tecnologia preferida simplesmente não pode conseguir se tornar popular e só atrair gênios da computação, eles não são muitos. Para ser popular ela vai precisar dos idiotas. E os idiotas têm um talento natural para estragar as coisas, lembra? Se você quiser ter uma tecnologia amplamente difundida, vai precisar aceitar que ela se torne o novo ASP. Não tem jeito.

Iterações pra que te quero!

Já há quem fale sobre a obsolescência das iterações como as conhecemos. Um dos maiores “culpados” por esse novo movimento é uma prática antiga da produção just-in-time: o Kanban. Mas este texto não é para falar de coisas tão novas assim. Isso aqui é sobre como boa parte da indústria ainda nem digeriu a idéia que veio antes: as iterações.

Mas espera aí. Todo mundo usa iterações, não?

Bom, na verdade não. Como podemos observar a partir de alguns relatos bem recentes, desenvolvimento de software em cascata ainda é realidade em muitos lugares. Pode parecer uma realidade bem distante para você, mas é algo bem próximo para essas pessoas. Tão próximo e tão incômodo que elas tomaram o tempo de sentar-se e escrever sobre o assunto. O pessoal do 1up4Developers até dedicou um site inteiro a isso. Eles deviam estar verdadeiramente sofrendo quando começaram isso.

A triste realidade é que não é tão incomum assim encontrar organizações onde uma “iteração” normalmente dura seis meses e onde é terminante proibido tocar em uma única linha de código enquanto a equipe não atravessar oficialmente a “fase de requisitos” (e talvez mais algumas outras fases depois dela). O pior é saber que o modelo da cascata nasceu somente como contra-exemplo (pdf), algo que deveria ser evitado tanto quanto possível. Não sei que seqüência de eventos esdrúxula consegue levar um modelo de contra-exemplo fabricado a prática padrão de toda uma indústria, mas foi o que aconteceu.

Felizmente, já existe uma solução para isto e ela, sem muita surpresa, atende pelo nome de iteração. Não iterações de seis meses para camuflar um processo em cascata, mas iterações de verdade que permitam feedback rápido e adaptação a mudanças. Antes que possamos começar a tornar as iterações obsoletas, precisamos apresentá-las a quem ainda não as conhece. Mais importante ainda: a quem acha que as conhece mas nunca viu nada além de desvirtuações do conceito.

Podemos até mesmo começar com cascatas iteradas. Já que eles parecem gostar da cascata, dividiremos os projetos em cascatas menores com entregas de software real ao final de cada ciclo. Todo mundo continua trabalhando do mesmo modo de sempre, mas agora os prazos para cada fase e o escopo da iteração como um todo serão reduzidos. A diferença é que os clientes terão software para observar mais cedo, algo real e bastante vivo que possam usar como base para novas idéias ao invés de serem obrigados a fazer especulações baseadas em documentos mortos. Certamente não é o ponto a que queremos chegar, mas quem disse que ia ser fácil?

Uma questão essencial para esta estratégia é o tamanho das iterações. Cascatas iteradas muito longas seriam somente casos especiais do processo de desenvolvimento anterior. Seis meses certamente é muito tempo e três meses ainda parece muito. A partir de um mês de duração começamos a conversar. Ainda é bastante longo, mas, novamente, é só um ponto de partida.

Depois disso o próximo passo provavelmente deve ser mostrar como representações intermediárias podem ser ineficientes. Novamente o encolhimento das iterações deve nos ajudar. Prazos de um mês já devem estar suficientemente difíceis de cumprir quando se tem representações intermediárias para criar e as equipes provavelmente já começaram a eliminar as práticas mais ineficientes neste ponto. Encolher as iterações para quinze dias deve dar conta de eliminar a maioria das ineficiências restantes. Você simplesmente não consegue produzir porções significativas de software funcionando se tiver que escrever treze documentos e aprovar cinco solicitações de mudança pra cada linha de código que precisar alterar.

Quando chegarmos a este ponto, será que deveríamos parar aí? A beleza deste processo de encolhimento das iterações é poder continuar diminuindo-as. Iterativamente, se você me permite. O limite será alcançado quando em uma iteração couber somente um requisito. Neste ponto, a única maneira de continuar encolhendo as iterações é encolher os próprios requisitos, tornando-os cada vez mais simples e atômicos.

Mas será que sua organização vai ter coragem para chegar lá?

4 respostas sobre controle de versão distribuído

Entre idas e vindas de servidores, domínios e engines, já faz quase três anos que este blog existe. Em todo este tempo ainda não fiz nenhum texto com um título do tipo “10 razões para usar Emacs ao invés de Vi” que parece ser tão comum nesse mundo.

Isso precisava mudar e meu texto anterior sobre controle de versão parece ter servido como a desculpa perfeita. Surgiram algumas perguntas e observações nos comentários que me parecem ser bem freqüentes e resolvi compilar as perguntas e as respostas em um texto no formato 10-mais. As perguntas estão parafraseadas aqui e não foram necessariamente redigidas assim originalmente.

Vamos às questões:

Como as empresas podem usar sistemas distribuídos e ainda ter controle sobre seu código fonte?

Entendo a suposta necessidade das organizações de manter um controle rígido e centralizado do código-fonte. É bem verdade que muitas vezes elas fazem isso não tanto por necessidade, mas simplesmente por teimosia ou pela boa e velha força do hábito (também conhecida como inércia corporativa), mas admitamos que seja o caso de realmente haver a necessidade de um local central para se chamar de oficial.

Elas poderiam não usar nenhum sistema de controle de versão e ainda ter controle sobre o código. Basta dizer aos programadores que o código só deverá ser considerado pronto quando for enviado para determinado local. Este local pode ser um repositório de controle de versão centralizado, mas também pode ser um site FTP, um compartilhamento de rede qualquer ou um pendrive na mesa do chefe. Não importa.

A centralização do local de publicação oficial não exige a centralização do sistema de versionamento. Se as empresas simplesmente exigissem que as equipes disponibilizassem o código em algum lugar acessível dentro da rede interna, os programadores poderiam escolher qualquer método que quisessem para controlar suas modificações. Eles poderiam transferir arquivos de máquina em máquina ou guardar diffs manualmente, mas eu poderia apostar que a maioria deles optaria por um sistema de controle de versão qualquer. Muitas empresas não deixam a equipe escolher sozinha por preferir investir menos no processo de seleção dos funcionários e simplesmente assumir posteriormente que todos são tapados incapazes de limpar o próprio nariz. Quando todo mundo é idiota, é melhor minimizar os graus de liberdade para evitar que eles acabem colocando o dedo na tomada. É melhor exigir que todo mundo use exatamente os mesmos procedimentos e ferramentas. Mas esta é uma questão para um texto futuro.

Não há nada impedindo que os sistemas distribuídos de hoje sejam usados de forma centralizada. A equipe pode muito bem escolher um local para chamar de central e oficial. Eles podem fazer isso, mas não são obrigados. A questão é que há limitações que os sistemas centralizados têm que os distribuídos não têm. Não manter histórico das alterações das cópias de trabalho locais e exigir acesso de rede para commitar são só duas delas…

Como o controle de patches facilita os merges? Se há conflitos, eles precisam ser tratados independentemente do modelo de controle de versão adotado…

Se duas pessoas estão trabalhando no mesmo trecho de código, os conflitos certamente serão os mesmos não importa qual o sistema de controle de versão que usem. Neste caso, elas provavelmente precisam reestruturar o código para evitar pisar nos pés uma da outra. O controle de patches faz uma diferença maior quando os merges precisam ser feitos periodicamente.

O controle de versão por patches (e aí não estou necessariamente falando de controle distribuído, como mostra o texto anterior) evita que você precise lembrar quais as modificações que você já integrou. Um bom exemplo é quando você está fazendo merges sucessivos, como no caso do branch de desenvolvimento que precisa incorporar as modificações feitas no branch de conserto de bugs. Com controle de patches, você pode derivar uma base de código qualquer, fazer modificações locais e incorporar de tempos em tempos as modificações feitas no código original com muito menos dor de cabeça. Você não precisa se lembrar de que ponto do código original começou, nem até onde foi seu último merge. Desde que possua todas as modificações que o repositório original tem, você está atualizado.

Na média os usuários não querem colaborar. Eles contornam o sistema de controle de versão, compartilham código por outros meios e fazem todo tipo de porcaria.

Mais um argumento a favor dos sistemas distribuídos. Dessa vez não só dos não-seqüenciais, mas dos verdadeiramente distribuídos mesmo. A razão das pessoas estarem compartilhando código fora do controle de versão muitas vezes é o fato do código simplesmente não estar pronto para ser publicado. Ele não pode ir para o repositório central ainda, mas não é por isso que deve deixar de ser compartilhado.

Digamos que estou trabalhando em algum tipo de reestruturação extensiva. Para fazer essa reestruturação, eu preciso deixar alguns testes falhando por algum tempo. Eu sei que, quando terminar, os testes vão voltar a passar, mas precisam ficar quebrados enquanto eu termino. Qualquer reestruturação deste tipo pode ser sub-divida em pequenos objetivos. Portanto, faz sentido registrar alguns marcos parciais. Porém, se o sistema é centralizado, eu não posso fazer isso para não atrapalhar meus colegas. Minha única alternativa passa a ser bifurcar a base de código por algum tempo até que eu consiga terminar a reestruturação. Posso fazer isso por meio de branches ou nem usar o controle de versão e compartilhar código pela rede interna de algum outro modo.

Enquanto estou fazendo essa reestruturação, pode ser que você precise de algumas modificações que eu fiz. É em horas como essas que o pessoal começa a compartilhar código pela rede, fazer merge manual e usar de uma imaginação incrível para contornar as limitações do sistema. Com controle distribuído, cada desenvolvedor tem um ou mais repositórios próprios — essencialmente branches pessoais — que, em termos de recursos de versionamento, não deixam nada a desejar em relação ao repositório central. Se você precisa das minhas alterações, você simplesmente as importa diretamente do meu repositório. Não precisamos usar o repositório central como local de troca se não quisermos. Desse modo só nós dois precisamos lidar com aqueles testes falhando, e não a equipe toda.

Programadores ruins gostam de empurrar mouse e precisam de interfaces gráficas intuitivas. Nem todas equipes são bem capacitadas.

Sobre interface gráficas, este não é um problema inerente à distribuição e pode ser resolvido. Na verdade, eu acredito que já venha sendo resolvido. Eu particularmente não tenho problemas para trabalhar na linha de comando, portanto nem chego a procurar esse tipo de front-end. Então não posso dizer com toda a certeza que este problema não existe, mas provavelmente já deve estar bem perto de ser eliminado.

Interfaces intuitivas certamente beneficiam a todos — aos rock-stars também — e devem ser uma busca constante. Mas se uma equipe é pouco capacitada, pode acreditar que a falta de interfaces gráficas não é o maior dos problemas.

A maldição da popularidade

Eu definitivamente não estou entre os mais antigos praticantes da indústria do desenvolvimento de software, mas já vi alguns fenômenos se repetirem o suficiente para desconfiar que devem ser alguma espécie de lei universal ou coisa parecida. Como sou um fanático por linguagens de programação, minha observação está nesse campo. Mais precisamente nas comunidades que se formam ao redor delas. Eu sei que comunidade é meio que uma palavra da moda hoje em dia, então pode abandonar este texto e passar para o próximo da sua lista de leitura de feeds de hoje: isso aqui certamente vai ser bem vazio e superficial.

Assim como qualquer coisa, linguagens de programação também passam por um período de maturação antes de se tornarem populares. No início elas só são usadas por verdadeiros geeks de linguagens que se divertem em procurar não a próxima grande revolução, mas boas idéias em termos de expressividade. Muitos deles nem chegam a usar direito as linguagens, preferindo aprofundar-se em uma e só ficar de olho em outra meia dúzia.

A maioria dessas pessoas atraídas nessa fase embrionária são programadores excepcionais e começam a ajudar a fazer várias das bibliotecas que vão servir de apoio para as massas que virão a usar a linguagem dali a alguns anos. Eles não fazem isso porque querem ser os senhores daquelas pequenas comunidades no futuro, mas simplesmente porque gostam e se divertem com isso. Afinal de contas, o que pode ser mais divertido do que fazer seu próprio cliente HTTP, por exemplo?

Não precisa responder.

Quando estão nesta fase, as listas de discussão, blogs e fórums sobre essas linguagens costumam ser bastante interessantes. O cara que está fazendo o cliente HTTP conversa com o cara que está tentando melhorar a biblioteca de coleções e todos têm idéias boas para dar. Toda semana alguém bola uma nova expressão idiomática elegante e você se impressiona cada vez mais. Começa a surgir o sotaque da linguagem.

Contraste isso com o que costuma acontecer cinco a dez anos depois se a linguagem chegar a se tornar popular. Neste ponto as listas de discussão começam a se repetir e parece que todo mundo só quer saber como redimensionar uma imagem para mostrar numa aplicação web.

Hora de partir para a próxima linguagem…

Não sei exatamente porque, mas as comunidades pequenas funcionam melhor. Talvez porque os geeks iniciais sejam simplesmente mais interessantes do que a horda de invasores que só querem “fazer um sisteminha” e usar a nova linguagem da moda. A linguagem não costuma mudar muito neste meio tempo. O sotaque usado pelos mais antigos continua mais ou menos o mesmo, mas a comunidade cresce. Com o aumento do número de participantes, qualquer um esperaria que aumentasse a quantidade de boas idéias. Mas ao invés disso, elas parecem diminuir. Ao invés de serem um grupo organizado de pessoas, as multidões se comportam mais como uma manada de touros: ficam pastando no mesmo lugar até que alguém resolva correr tresloucadamente para um lado, hora em que todo mundo decide fazer o mesmo e que alguns são atropelados no meio do processo.

Quando comecei a me interessar por Ruby, era bastante instrutivo acompanhar a ruby-talk. Dava pra distinguir facilmente a voz de gente como Jim Weirich e Hal Funton. Agora, três anos e várias reportagens em grandes revistas depois, tudo o que se vê nos mais variados fóruns da linguagem são as mesmas perguntas sobre como estabelecer relacionamentos NxN. Pelo menos a comunidade Ruby ainda não chegou na proporção da Java, onde encontra-se facilmente gente tentando desenhar diagramas de seqüência para qualquer porção de código que precise ser escrita.

Há coisas que fazem bem para uma linguagem de programação e ser citada em revistas de grande circulação não é uma delas. Isso com certeza faz com que mais gente tome conhecimento, mas as pessoas que importam já haviam sido apresentadas à tecnologia por outros meios. Estas são as pessoas que lêem, pesquisam e estão sempre tentando permanecer atualizados em relação a sua arte. Muito antes dos grandes canais descobrirem as novas tecnologias, elas já sabiam delas através de canais mais alternativos (e mais rápidos e vibrantes) como blogs, listas de discussão e fóruns. São essas pessoas que têm as idéias brilhantes, que escrevem as primeiras bibliotecas, que determinam o rumo das comunidades nascentes, enfim, que realmente fazem a diferença.

Claro que os grandes canais ajudam a dar visibilidade às tecnologias e tornam as coisas mais fáceis para os infelizes que precisam convencer doze níveis de gerência antes de usar qualquer novidade, mas a tecnologia em si não necessariamente evolui mais rápido por causa disso. As pessoas que fazem alguma diferença seriam atraídas pelos méritos da tecnologia de qualquer modo, sem precisar do catalisador da popularidade. A popularidade tem suas vantagens, mas é uma maldição disfarçada. Gente demais na maioria das vezes atrapalha ao invés de ajudar, obrigando os geeks do início a fazer malabarismos para encontrar o que ainda há de relevante e afastando os novos geeks que teriam boas idéias com todo o barulho.

Por um controle de versão menos insano

Recentemente encontrei um texto do Jeff Atwood sobre a instalação do Subversion. Ele tem alguns argumentos bastante interessantes sobre controle de versão (sempre use controle de versão, faça modificações pequenas e blá, blá, blá…) que me parecem em geral bastante acertados, mas uma coisa me chamou a atenção: por que um programador (que pelo menos parece) tão bem informado quanto ele ainda escolhe um sistema de controle de versão tão completamente insano quando já existem tantas alternativas mais naturais para qualquer ser humano?

A maioria dos sistemas da velha guarda incrementa algum tipo de contador para identificar cada versão do objeto controlado: o Subversion, por exemplo, gera números de revisão quando há modificações em alguma parte da árvore de diretórios e o CVS associa um identificador seqüencial a cada um dos arquivos controlados. O maior problema do versionamento seqüencial é que simplesmente não controla o que queremos que controle. Saber quantas vezes um item qualquer foi modificado é útil, mas não tão útil quanto saber quais foram as modificações e, ainda melhor, quais as dependências entre elas. Sim, é bastante interessante saber qual o estado final de um trecho de código após um certo número de transformações, mas me interessa muito mais saber o que cada uma dessas modificações faz e tratar as modificações como objeto de trabalho, não os resultados delas.

Mais do que quantas modificações aconteceram desde algum ponto qualquer do tempo, eu preciso saber se a segunda modificação pode ser aplicada sem a primeira, se a terceira é uma inversão da primeira ou se a quinta desfaz algumas coisas que a quarta fez. As respostas para todas estas perguntas são necessárias para a operação mais fundamental do controle de versão: o merge.

Quando o Subversion surgiu, lá pelos idos de 2004, um de seus maiores argumentos era o branch barato. Este com certeza é um bom recurso para se ter, afinal de contas derivações precisam ser criadas o tempo todo. Mas o que queremos mesmo é que o merge seja fácil. O branch é só o começo da história. O merge é o ponto alto do processo, é quando as contribuições dos vários envolvidos são combinadas e passam a (pelo menos tentar) funcionar em conjunto. Há uma palestra em que Linus Torvalds resume isto muito bem em algumas poucas palavras: “branches são completamente inúteis, a não ser que você faça o merge”. Só que o versionamento seqüencial pára no branch e o merge precisa ser feito de forma manual porque o sistema não enxerga as dependências entre as alterações, só sabe qual delas aconteceu antes ou depois. Você pode conseguir ajuda para comparar duas versões do mesmo conteúdo, mas é você quem tem que saber quais devem ser as duas versões para este merge em particular que você quer fazer. A coisa fica ainda mais complicada quando você precisa importar modificações de outro branch periodicamente. Quando, por exemplo, seu projeto tem um branch estável em que só se faz conserto de bugs e outro — ou outros — em que são desenvolvidos novos recursos e que precisa receber as mesmas modificações que o estável para se manter livre de bugs. As pessoas acabam desenvolvendo algumas soluções arcanas para controlar manualmente quais alterações já foram aplicadas a quais linhas de desenvolvimento, coisa que o sistema de controle de versão podia (e deveria) fazer sem precisar de babá.

Os identificadores seqüenciais precisam ser atribuídos por algum tipo de servidor central. Não podem ser determinados por máquinas diferentes para evitar conflitos de nomes e acabam sendo completamente artificiais porque refletem simplesmente a ordem em que o servidor recebeu as alterações. Para a maioria das alterações, a ordem de aplicação pouco importa. E quando ela importa, os sistemas com versionamento seqüencial não costumam ajudar.

A alternativa ao controle seqüencial é o controle de patches (termo que admito estar inventando agora, então não deve ser muito preciso e provavelmente você vai encontrar isto com outro nome por aí). Um sistema de controle de patches associa um identificador a uma modificação, não ao resultado dela como os de controle seqüencial. Uma versão qualquer é simplesmente um acumulado de modificações que, quando combinadas, determinam um resultado final.

Sistemas seqüenciais são necessariamente centralizados. Não há como decidir qual o número para a próxima versão se não houver uma autoridade central para controlar a numeração. Porém, o inverso não é verdadeiro. Isto é, sistemas baseados em controle de patches não precisam ser distribuídos. A maioria dos que encontramos realmente é, mas não precisavam ser. A questão é que eles podem ser muito bem usados como sistemas centralizados, mas já tem tudo que precisam para serem distribuídos e independentes de um servidor central. Então porque se limitar? Além de poderem fazer tudo que os centralizados conseguem, os sistemas distribuídos ainda costumam ser muito mais fáceis de instalar. Se o Atwood tivesse escolhido um deles, não precisaria nem escrever um tutorial de instalação para si próprio. Era só escolher inicializar um diretório qualquer como repositório e começar a versionar o que quisesse. Sem serviços. Sem nomes de usuário. Sem senhas. Sem dores de cabeça com o firewall.

Minha impressão é que o funcionamento dos sistemas distribuídos de hoje é muito mais parecido com o modo como naturalmente pensamos do que o dos centralizados. Quando quisesse combinar duas linhas de trabalho distintas eu deveria apenas dizer a meu sistema que quero combinar as modificações que eu tenho com as que meu colega tem e ele deveria ser capaz de fazer isso sozinho (assumindo que não haja conflitos complicados). Eu não deveria precisar criar marcadores artificiais e manter manualmente um histórico de quais alterações dele eu já tenho. Por que algumas vezes ainda insistimos em usar sistemas centralizados? Há alguma vantagem oculta neles ou o quê?

Seja bem-vinda, manutenção!

É bastante comum ver organizações que costumam desenvolver software em cascata espernear quando chega a hora de implantar e colocar os sistemas em produção. Depois que tudo está instalado e funcionando a todo vapor elas acabam entrando em modo bombeiro e passam simplesmente a apagar um incêndio atrás do outro, sem saber muito o que fazer e como organizar seus esforços de manutenção.

Isto costuma acontecer porque a abordagem de desenvolvimento delas simplesmente não está adaptada à manutenção. Ela foi otimizada para um cenário (completamente fictício, diga-se de passagem) onde não há vida após a entrega do sistema, onde os clientes não mudam de idéia e onde não acontecem requisições de mudança depois que o software está pronto.

Não importa quanto alguém se dedique à tarefa. Ninguém consegue fazer a água da cascata cair para cima.

A abordagem oposta, obviamente, é otimizar para a manutenção, isto é, estar pronto para começar pequeno, mudar sempre que necessário, consertar o software aos poucos e torcer para que um dia ele não precise mais de conserto. A proposta Ágil começou com experiências neste sentido, tomando emprestado da filosofia release early, release often e preferindo usar o próprio software para comunicação entre os clientes e a equipe de desenvolvimento ao invés de documentos e diagramas. Num certo sentido, equipes ágeis tomam a manutenção como modo de operação normal no lugar do desenvolvimento puro. Preferem desenvolver uma solução completa e usável para um problema pequeno rapidamente para que possam dar as boas vindas à manutenção o mais cedo possível. Enquanto os clientes não tem uma peça real de sofware para usar e experimentar, suas sugestões são só um pouco melhor do que especulação.

Esta não é uma idéia tão louca no fim das contas. Pensando bem, da segunda linha de código para a frente tudo é manutenção.

O que diabos é AGPL?

A menos que você tenha passado os últimos dez anos preso em uma caverna em um planeta distante com os olhos e ouvidos vendados, deve pelo menos já ter ouvido falar da General Public License, a famigerada GPL. Ela (ainda) deve ser a licença mais usada no mundo para distribuir software livre e suas condições são bastante simples:

  1. Você tem acesso ao código-fonte do programa que está executando e pode fazer basicamente qualquer coisa com ele;
  2. Você pode inclusive modificar o programa, mas se quiser fazer isso precisa distribuí-lo sob os mesmos termos.

O sistema é bem elegante: você pode modificar os programas que recebe e/ou redistribuí-los sem modificação, mas deve oferecer a quem receber o programa de você os mesmos direitos que você recebeu. Isso garante que toda melhoria esteja disponível para todos os interessados e acaba criando comunidades em torno de uma peça de software. Claro que o texto completo da licença é bastante extenso, afinal de contas precisa incluir todo o advoguês para cobrir os casos limite e garantir que ninguém perca nenhum desses direitos no meio do caminho, mas a idéia é basicamente esta.

Porém há um detalhe bastante sutil neste esquema todo. Com a GPL, as pessoas precisam ter acesso ao código-fonte dos programas que estão executando. No caso de programas acessados através de uma interface de rede qualquer quem executa o código é uma máquina remota. Os usuários que acessam a aplicação de outras máquinas numa rede não podem ver o código-fonte e o dono do servidor pode modificar o programa o quanto quiser sem precisar distribuir o código-fonte. Afinal de contas, tecnicamente é só ele e mais ninguém que executa o programa. É completamente legal, por exemplo, que alguém baixe a versão 0.6.10 do Motiro, faça algumas modificações locais e sirva a aplicação para quem quiser usá-la sem ver o código-fonte correspondente.

As aplicações web, como o Motiro, são certamente o exemplo mais óbvio de onde este detalhe pode ser explorado para subverter as intenções iniciais do autor e, como parece que tudo atualmente está indo parar na web, ele ficou bastante evidente. Também é importante notar que é bem possível que o autor realmente quisesse deliberadamente permitir este uso, mas vou deixar esta discussão para outro dia. No caso do autor que quer que todos os usuários possam estudar e contribuir com seu programa, o universo de colaboradores pode ser bastante reduzido se forem considerados usuários apenas os donos dos servidores.

Colocando de uma forma bastante grosseira (e abrindo espaço para quem quiser corrigir), a GPL considera que os usuários são as pessoas que detém as máquinas que executam os programas. Neste sentido, os usuários têm todos os seus direitos, inclusive o acesso ao código-fonte, garantidos. Porém, no caso das aplicações web, as pessoas que realmente usam os sistemas (tipicamente através de seus navegadores) não têm nenhum desses direitos.

Num mundo repleto de processamento distribuído onde a maioria das pessoas não sabe exatamente que computador está efetivamente executando os programas que elas usam, a distinção entre executores e usuários deixa de fazer tanto sentido. A GNU Affero General Public License é uma licença com texto baseado na GPL original (dê uma olhada no texto completo para saber os detalhes, eu não sou advogado) que tenta consertar esta brecha. A história da licença é bem complexa, mas se eu tivesse que resumir, diria que para a AGPL não importa se as pessoas estão executando um programa em um processador local e usando os sistemas através dos seus teclados e monitores ou se acessam um servidor através da Internet; não importa se elas estão executando o programa por conta própria ou se usam do serviço de hospedagem oferecido por outra pessoa, se puderem efetivamente usar um programa, são consideradas usuários e, como tal, devem ter acesso ao código-fonte.

(Uma observação relacionada: consegui permissão dos demais autores do Motiro para mudar a licença de distribuição e hoje publiquei a versão 0.6.11 do programa, agora sob AGPL. Se estiver precisando de um sistema para acompanhamento de projetos, considere usar o Motiro.)

A evidência definitiva da programação saudável

Como alguém pode saber que uma equipe de programadores desenvolve software com um processo saudável? Como podemos saber se as práticas adotadas pela equipe são suficientes para produzir software útil, correto e de fácil manutenção? Quais as evidências que devemos procurar para separar os bons projetos dos ruins?

Estas são questões fundamentais tanto para quem está procurando um fornecedor como para quem está projetando um novo selo ou certificado parecido com os milhares que já temos para identificar organizações que sabem desenvolver software. Quem se propõe a fornecer um selo desse tipo precisa observar o trabalho dos candidatos por algum tempo para tentar encontrar algumas evidências de um processo saudável e sustentável. Precisa procurar pelos rastros deixados por um trabalho bem feito.

Uma forma razoável de identificar estes rastros é projetar um processo de desenvolvimento que comprovadamente dá certo (pelo menos para alguns casos) e verificar o que uma equipe que usou este processo deixou pelo caminho. Alguns selos em uso atualmente fazem exatamente isso: eles certificam toda uma classe de processos que produzem os mesmos subprodutos que um processo original. As equipes que querem ser certificadas precisam aprender o modo de operação e repetí-lo, produzindo evidências concretas de que seguiram um processo aprovado pelo certificador. Documentos de requisitos, relações de casos de uso, matrizes de rastreabilidade, diagramas de relacionamento e relatórios de revisão são todos exemplos de evidências bastante tangíveis da existência de procedimentos definidos.

Porém nenhum desses documentos pode ser considerado uma evidência definitiva da qualidade do processo. Nenhum deles consegue provar sozinho que o processo de desenvolvimento funciona.

Nem mesmo todos eles juntos conseguem fazer isso.

A evidência definitiva de um processo de desenvolvimento de software saudável é bastante óbvia, mas isso não quer dizer que todo mundo consegue enxergá-la. O único produto suficiente e necessário para identificar que um processo realmente dá certo é simplesmente software funcionando.

Só isso.

Não são documentos de requisitos bem escritos, matrizes de rastreabilidade gigantescas, nem diagramas bonitos e coloridos. A maior evidência que se pode observar em projetos saudáveis é a simples entrega de software funcionando. Deve ser suficiente observar que uma equipe consegue entregar software funcionando a cada, digamos, uma ou duas semanas para saber que eles têm uma boa abordagem para desenvolvimento de software. Uma queda da velocidade de evolução do sistema é o que basta para saber que algo vai errado.

Nem todos os processos de desenvolvimento são projetados para entregar software funcionando dentro de períodos curtos de tempo. Por isso desenvolveram-se vários modelos que verificam subprodutos intermediários como diagramas de estado e especificações funcionais. É um caso clássico de quem não tem cão, caça com gato. Estes subprodutos servem como promessas, garantias, evidências de que em algum momento futuro o sistema será entregue como esperado. O problema é que nenhum subproduto desses é tão verificável quanto o produto final. Eles servem para tranqüilizar um pouco os clientes, que de um modo ou de outro recebem algumas garantias de que seu dinheiro está sendo bem gasto. Mas definitivamente não servem para dar certeza do sucesso do projeto, nem ao menos da transformação do produto em software funcionando.

Orientação a objeto, e daí?

Não é tão incomum sugerir algo novo para um programador “orientado a objetos” e escutar alguma variante da velha frase “mas isso não vai de encontro a todas as noções conhecidas de orientação a objetos?”. Pode acontecer ao mostrar linguagens dinâmicas que permitem redefinição de métodos em tempo de execução, ao demonstrar as vantagens das closures ou até ao introduzir interfaces fluentes. Você escolhe.

Não precisamos nem discutir o que diabos é orientação a objetos para notar que o termo está perdendo o sentido mais rápido do que um raio. Nenhum dos exemplos citados parecem ofender o meu conceito de orientação a objeto e também não vejo porque feririam o de ninguém. Para ser sincero, desde quando os objetos tornaram-se tão sagrados que não possam ser subvertidos um pouco de vez em quando? Apesar disso já vi este argumento ser usado em todos os três casos.

O ponto aqui não é que orientação a objetos não sirva para nada. Nem que o purismo deva ser evitado. Na verdade, o purismo pode ser bastante útil como ferramenta intelectual. Duvido, por exemplo, que muitos dos recursos interessantes introduzidos em Haskell surgissem se não houvesse um compromisso com a pureza da linguagem em termos de transparência referencial. O grande problema com a orientação a objeto não é o purismo.

É o sucesso.

Com a popularização na última década da orientação a objeto o termo se disseminou. Isso fez mal para ele porque quanto mais gente conhece um termo fracamente definido, maiores as chances de ser desvirtuado ou simplesmente usado sem sentido nenhum. Parece que orientação a objeto acabou virando argumento contra qualquer coisa e, como geralmente ninguém sabe a que definição o falante está se referindo, não sobram muitos contra-argumentos. Nas poucas vezes que uma discussão mais ou menos civilizada é possível, o resistente acaba não se convencendo de verdade. Seria melhor que as pessoas dissessem que não gostaram da idéia, mas “estou sendo apresentado a esta técnica agora, parece diferente de tudo que já vi, estou com medo e não quero avaliar melhor as alternativas” não parece tão profissional. Sobra para a velha e leal orientação a objetos.