A filosofia Unix
Um livro chamado The Art of Unix Programming já foi recomendado algumas vezes aqui no TabNews, e após ver este comentário do clacerda, decidi ler o livro.
É um livro grande, em inglês, e a versão online é disponibilizada de forma gratuita pelo autor Eric Steven Raymond.
Esta publicação aborda o primeiro capítulo do livro, mais especificamente o tópico Basics of the Unix Philosophy, e não substitui a leitura do mesmo. Decidi trazer este conteúdo porque foram mencionadas 17 regras (ou princípios) que podem ser aplicadas durante o desenvolvimento de um sistema, e muitas dessas regras também são recomendadas por outros autores. É o tipo de coisa que talvez você descobriria sozinho depois de alguns anos de experiência enfrentando dificuldades.
Noções básicas da filosofia Unix
A filosofia Unix não é um método formal de como projetar sistemas. Ela é baseada na experiência. O Eric Raymond pegou frases e regras ditas pelo Douglas McIlroy, o inventor dos pipes Unix e um dos fundadores da tradição Unix, do Rob Pike, que se tornou um dos grandes mestres de C, e do Ken Thompson, que projetou e implementou o primeiro Unix. Além disso, como havia mais da filosofia Unix de forma implícita, não no que foi dito, mas sim no que foi feito e no exemplo que o próprio Unix deu, Eric Raymond abstraiu as 17 regras abaixo.
1. Regra da Modularidade
Escreva partes simples conectadas por interfaces limpas.
A única forma de escrever software complexo que não falha é mantendo uma baixa complexidade global — construir a partir de partes simples conectadas por interfaces bem definidas, de forma que a maior parte dos problemas são locais e você pode ter alguma esperança de atualizar uma parte sem quebrar o todo.
2. Regra da Clareza
Clareza é melhor do que inteligência.
Como a manutenção é uma parte importante e cara do desenvolvimento, escreva o sistema de forma que a comunicação mais importante não é do código para com a máquina, mas sim do código para com o desenvolvedor que manterá o código no futuro.
"Nunca lute para decifrar código sutil três vezes. Uma vez pode ser um golpe de sorte único, mas se você se encontra tendo que entendê-lo pela segunda vez — porque a primeira vez foi há muito tempo e você esqueceu os detalhes — é hora de comentar o código para que a terceira vez seja relativamente sem dor." — Henry Spencer.
Essa recomendação vai além de comentar o código. Escolha seus algoritmos e implementações visando a manutenção futura. Ao aceitar um pequeno aumento no desempenho em troca de um grande aumento na complexidade, você está aceitando um código com maior probabilidade de abrigar bugs, além de um código mais difícil de ler para futuros mantenedores.
3. Regra da Composição
Projete programas para estarem conectados com outros programas.
A tradição Unix incentiva fortemente a escrita de programas que leiam e escrevam formatos simples, textuais, orientados a fluxo e independentes de dispositivo. Isso porque, se você não escrever programas que aceitem e emitam fluxos de texto simples, será muito mais difícil conectar os programas.
Antes de escrever uma interface gráfica, pergunte-se se as partes interativas complicadas do seu programa podem ser segregadas em uma parte e os algoritmos robustos em outra, com um simples fluxo de comando ou protocolo de aplicativo conectando as duas partes.
Tentando trazer essa regra para os dias atuais, imagino que podemos pensar em integrações de APIs, componentes de UI, plugins, extensões e outros programas similares.
4. Regra da Separação
Separe a política do mecanismo; separe interfaces de engines.
A política e o mecanismo tendem a mudar em escalas de tempo diferentes, com a política mudando muito mais rápido do que o mecanismo. O frontend implementa a política; o backend, o mecanismo. A complexidade global do par geralmente será bem menor do que um único processo monolito implementando todas as funções.
5. Regra da Simplicidade
Projete para simplicidade; adicione complexidade apenas quando necessário.
A complexidade pode vir de programadores se orgulhando de serem capaz de lidar com certas complexidades e abstrações, ou então de requisitos de projeto baseados na moda do marketing do mês, ao invés de focar no que os clientes desejam ou do que o software pode realmente oferecer. No fim, todos perdem.
Para evitar isso, encoraje uma cultura de sotware que saiba que o pequeno é bonito, e que resista ao inchaço e à complexidade.
6. Regra da Parcimônia
Escreva um programa grande apenas quando é claro e por demonstração que nenhuma outra coisa servirá.
Permitir que os programas sejam grandes prejudica a manutenção. Como as pessoas relutam em jogar fora um produto que veio de muito esforço e trabalho, os grandes programas convidam ao investimento excessivo em abordagens que falham ou são subótimas.
7. Regra da Transparência
Projete para visibilidade para facilitar a inspeção e a depuração.
Um sistema de software é transparente quando você pode olhar para ele e imediatamente entender o que ele está fazendo e como. Isso é detectável quando existem facilidades de monitoramento que mostram o estado interno de forma que seu programa não apenas funciona bem, como pode ser visto funcionando bem.
8. Regra da Robustez
Robustez é a filha da transparência e simplicidade.
A maioria dos sistemas são frágeis e contém bugs porque a maioria dos programas são complicados demais para um cérebro humano entender de uma vez. Quando você não consegue raciocinar corretamente sobre as entranhas do programa, você não consegue ter certeza se está correto, e você não pode consertar algo que não está quebrado.
"A entrada de dados gerada por outros programas é notória para testes de estresse do software (…). As formas envolvidas geralmente parecem inúteis para humanos. Por exemplo, aceitar uma lista vazia, ou uma string vazia, etc., mesmo em lugares onde um humano raramente ou nunca forneceria isso, evita situações de tratamentos de casos especiais quando a entrada de dados é gerada de forma mecânica." — Henry Spencer
9. Regra da Representação
Junte o conhecimento aos dados para que a lógica do programa possa ser estúpida e robusta.
O dado é mais rastreável do que a lógica do programa. Onde você vê uma escolha entre complexidade na estrutura de dados e complexidade no código, escolha a primeira. Na evolução de um design, você deve ativamente procurar formas de mudar a complexidade do código para o dado.
10. Regra da Menor Surpresa (ou Regra do Menor Espanto)
No design de interface, sempre faça a coisa menos surpreendente.
Os programas mais fáceis de usar são aqueles que exigem menos aprendizado do usuário – ou seja, são aqueles que se conectam de forma mais eficaz ao conhecimento pré-existente do usuário.
11. Regra do Silêncio
Quando um programa não tem nada de surpreendente a dizer, não deve dizer nada.
Programas bem desenhados tratam a atenção e a concentração do usuário como um recurso precioso e limitado, que só deve ser utilizado quando necessário.
"Quando a saída do seu programa se torna a entrada de outro, deve ser fácil obter os bits necessários." — Ken Arnold.
12. Regra do Reparo
Quando você precisar falhar, falhe ruidosamente e o mais rápido possível.
Escreva seu software para lidar com entradas incorretas e seus próprios erros de execução da maneira mais elegante possível. Mas quando isso não for possível, faça-o falhar de uma forma que torne o diagnóstico do problema o mais fácil possível.
"Seja liberal no que você aceita e conservador no que você envia." — Jon Postel.
13. Regra da Economia
O tempo do programador é caro; conserve-o em preferência ao tempo da máquina.
O autor diz que se levarmos essa máxima realmente a sério durante o desenvolvimento de software, a maioria das aplicações seriam escritas em linguagens de alto nível como Perl, Tcl, Python, Java, Lisp e até mesmo shell — linguagens que aliviam a carga do programador fazendo seu próprio gerenciamento de memória.
Outra maneira de conservar o tempo do programador é ensinar às máquinas como realizar a maior parte do trabalho de programação de baixo nível.
14. Regra da Geração
Evite hackear à mão; escreva programas para escrever programas quando puder.
Código gerado (em todo nível) é quase sempre mais barato e confiável do que um hackeado à mão.
Vale a pena usar geradores de código quando eles podem aumentar o nível da abstração — isso é, quando a linguagem de especificação para o gerador é mais simples do que o código gerado, e o código não precisa ser hackeado à mão após isso.
Essa regra me lembra de um cenário onde é preciso testar o desempenho de uma aplicação. Fica bem mais fácil fazer isso após criar um script para popular o banco de dados de forma automática.
15. Regra da Otimização
Prototipe antes de polir. Faça funcionar antes de otimizar.
Muitas vezes, a otimização local prematura na verdade dificulta a otimização global (e, portanto, reduz o desempenho geral). Uma parte prematuramente otimizada de um design frequentemente interfere em mudanças que teriam resultados muito maiores em todo o design, de modo que você acaba com desempenho inferior e código excessivamente complexo.
"A prototipagem é importante tanto para o projeto do sistema quanto para a otimização — é muito mais fácil julgar se um protótipo faz o que você deseja do que ler uma especificação longa." — Mike Lesk
“Um dos meus dias mais produtivos foi jogar fora 1.000 linhas de código.” — Ken Thompson
Essa regra me lembrou a pergunta Até que ponto a otimização prematura é um problema?, no Stack Overflow em Português. Fica a recomendação para maior aprofundamento.
16. Regra da Diversidade
Desconfie de todas as reivindicações de "um caminho verdadeiro".
Mesmo as melhores ferramentas de software tendem a ser limitadas pela imaginação dos seus designers. Ninguém é inteligente o suficiente para otimizar tudo, nem para antecipar todos os usos que seu software poderá ter.
"Não existe um desenvolvimento único, seja em tecnologia ou em técnica de gestão, que por si só prometa pelo menos uma melhoria de ordem de grandeza dentro de uma década em produtividade, em confiabilidade, em simplicidade." — Frederick Brooks.
17. Regra da Extensibilidade
Projete para o futuro, porque ele chegará mais cedo do que você imagina.
Nunca presuma que você tem a resposta final. Portanto, deixe espaço para que seus formatos de dados e código cresçam; senão, muitas vezes você descobrirá que está preso a escolhas iniciais imprudentes porque não pode alterá-las enquanto mantém a compatibilidade com versões anteriores.
Quando você projeta código, organize isso de forma que desenvolvedores futuros serão capazes de conectar novas funções na arquitetura sem precisar desfazer e reconstruir a arquitetura.
Em resumo, KISS.
Essas são apenas algumas partes do que o autor escreveu sobre cada regra, onde eu traduzi e também trouxe citações que achei pertinentes. Além disso, alguns parágrafos contêm o meu ponto de vista, o que fica claro pelo uso da primeira pessoa do singular.
Ainda não terminei o livro, mas tem sido uma boa leitura, então reforço a recomendação.