Conteúdos

Engenharia de Software na Web: os 12 fatores

Criar software não é tarefa fácil. É preciso dominar um conjunto grande de técnicas e tecnologias, o que acaba criando profissionais especializados em partes do processo, como backend, frontend e operações. Mas conhecer o processo completo de desenvolvimento de software é fundamental para que qualquer profissional possa fazer um trabalho bem feito. Com o advento da nuvem e todas as vantagens que ela traz, o processo modificou-se, e esse artigo apresenta a metodologia de 12 fatores para desenvolver e publicar softwares na nuvem.

A revolução do Software como Serviço

Por muito tempo, a maioria dos softwares eram individualmente instalados nas máquinas dos usuários, muitas vezes precisando de instalar além do software todos os frameworks que o acompanham. O advento da cloud computing mudou esse cenário.

Software como serviço (software as a service ou SaaS) é uma forma diferente de disponibilizar software. Não é mais necessário instalar o software na máquina do usuário; ele é executado na Web, geralmente em servidores da nuvem pública (AWS, Google Cloud, Azure, etc.), e para o usuário não é necessário mais do que um navegador.

A Heroku é uma empresa que surgiu em 2007, com a promessa de descomplicar o lançamento de aplicações na nuvem. O problema é que a maioria dos desenvolvedores não faziam aplicativos que se adequassem a esse novo contexto. Com a experiência adquirida, os engenheiros da Heroku lançaram um manifesto chamado “The Twelve-Factor App”, que consiste em 12 recomendações para a construção de aplicações para a nuvem.

A grande vantagem dos 12 fatores é que eles permitem desfrutar dos benefícios que a nuvem oferece, como resiliência, portabilidade e escalabilidade. Além disso, essas recomendações são independentes de linguagem de programação ou stack de desenvolvimento.

Os 12 fatores

1. Base de código

Uma base de código com rastreamento utilizando controle de versões, muitos deploys

Uma aplicação moderna, que segue os 12 fatores, deve ter sua base de código gerenciada por algum sistema de controle de versões como Git e Subversion.

Uma base de código não deve conter mais do que uma aplicação. Caso se trate de uma aplicação com muitos componentes distribuídos, as dependências devem ser isoladas e incluídas a partir de um gerenciador de dependências.

Existem muitas vantagens de se utilizar um sistema de controle de versões, como rastreabilidade e organização, mas para os 12 fatores o versionamento tem um caráter ainda mais relevante, visto que uma implantação (deploy) é uma instância da aplicação em um ponto da árvore de revisões. Assim, com um sistema de controle de versões é possível ter inúmeras versões da aplicação em execução, a partir de uma mesma base. Por exemplo, no Git, você pode ter sua aplicação executando a partir do código da branch master, ao mesmo tempo em que desenvolve novas funcionalidades na branch develop.

2. Dependências

Declare e isole as dependências explicitamente

Uma aplicação não deve incorporar uma dependência, mas deve fazer interface com ela a partir de um manifesto de declaração de dependências, que é, geralmente um arquivo em que todas as dependências são listadas, como o package.json do Node.Js, o *.csproj do .NET, e os arquivos *.pom do Java.

Isso vale também para a dependência do sistema operacional. Uma aplicação que segue os 12 fatores não deve depender dos comandos do sistema. Por exemplo, uma aplicação não deve fazer um curl diretamente, mas sim depender de uma versão exclusiva desse programa.

Esse princípio, se levado a nível de design de código, nos leva ao Princípio da Inversão da Dependência do SOLID, que diz que classes devem depender de abstrações e não de implementações.

3. Configurações

Armazene as configurações no ambiente

Toda aplicação possui elementos que variam dependendo do ambiente em que estão. Esses elementos são parte das configurações de uma aplicação. Uma aplicação que segue os 12 fatores deve saber separar essas configurações da aplicação em si.

Se você utiliza uma classe de constantes para armazenar strings de conexão com o banco de dados, sua aplicação não segue os 12 fatores. Mesmo arquivos de configuração não são recomendados, pela chance de colocar um arquivo de configuração por engano no repositório. De todo modo, é muito melhor um arquivo de configuração do que uma classe de constantes.

Para seguir os 12 fatores, sua aplicação precisa utilizar configurações como variáveis de ambiente. A grande vantagem é que variáveis de ambiente são a abstração perfeita para a portabilidade que as aplicações 12 fatores requerem. Elas não dependem de sistemas operacionais e são fáceis de ser alteradas.

4. Serviços de Apoio

Trate os serviços de apoio como recursos anexados

Uma aplicação geralmente possui muitos serviços de apoio. Um serviço de apoio é qualquer serviço externo que a aplicação precisa para funcionar, como um banco de dados (MySql, DynamoDB, etc.), um serviço de envio de email, filas e sistemas de mensagem (RabbitMQ, Kakfa, etc.), ou mesmo a consulta em alguma API externa.

Assim, reforçando a gestão de configurações, sua aplicação não deve requerer nenhuma mudança de código ao trocar, por exemplo, a variável de ambiente que armazena a connection string de uma base de dados MySql. Obviamente eles devem ser compatíveis, isto é, manter o mesmo contrato.

O que esse fator quer realçar é que qualquer elemento que esteja fora da aplicação deve ter pouco acoplamento com ela, podendo ser substituído a qualquer momento por outro equivalente.

5. Construa, lance, execute

Separe estritamente os builds e execute em estágios

Para implantar uma aplicação 12 fatores, uma base de código de aplicação passa por três estágios: construção (ou build), lançamento (ou release), execução (ou run). O estágio de construção transforma o código fonte em executável; o estágio de lançamento pega o executável gerado pelo estágio de construção e combina com a configuração desejada para a implantação; por fim, o estágio de execução faz a execução da aplicação em um ambiente determinado.

Além disso, cada lançamento (release) deve ter um identificador único, que permita ao desenvolvedor voltar para um estado anterior em caso de necessidade. No entanto, não podem ser alterados, é como uma palavra dita: não é possível voltar atrás e mudar a palavra dita mesmo há segundos atrás; mas é possível lançar uma correção do que se disse.

6. Processos

Execute a aplicação como um ou mais processos que não armazenam estado

Uma aplicação 12 fatores não deve armazenar um estado, mas executar como um processo sem estado, podendo ser substituído por outro equivalente sem nenhum impacto.

Algumas aplicações usam, por exemplo, a memória para armazenar a sessão do usuário. Essa é uma grave violação desse fator, pois ao precisar escalar a aplicação somente a instância que armazenou a sessão teria acesso a ela. Ao invés disso, aplicações 12 fatores devem usar serviços de apoio para armazenar estados, como banco de dados e serviços de cache (Redis, Memcached).

7. Vínculo de Porta

Exporte serviços via vínculo de portas

Uma aplicação que segue os 12 fatores é auto-contida e não depende de servidores web externos. Elas devem estar comunicáveis a partir de portas de rede. A grande vantagem desse fator é que qualquer aplicação pode se tornar um serviço de apoio para outra aplicação.

Quando você coloca sua aplicação em um container Docker, por exemplo, você cria um vínculo de porta com o mundo fora do container.

8. Concorrência

Escale através do modelo de processos

Como sua aplicação pode ser vista como um processo, então é ideal que ela seja dividida em tantos processos (aplicações) quanto necessários para permitir a escalabilidade e a concorrência.

Talvez uma parte de uma única aplicação seja mais utilizada do que outra. Por que escalar um processo gigante, se você poderia escalar apenas um processo menor?

9. Descartabilidade

Maximize robustez com inicialização rápida e desligamento gracioso

Você já deve ter percebido como um fator está em harmonia com os outros, como em uma orquestra. Assim, como aplicações 12 fatores não dependem de estado e funcionam como processos, elas podem ser descartadas a qualquer momento e substituídas por um outro processo compatível.

Por isso, as aplicações devem minimizar o processo de inicialização, permitindo agilidade e robustez no processo de escalonamento. E também devem saber finalizar sem efeitos colaterais: é preciso responder a todas as requisições em andamento e finalizar todas as transações.

10. Paridade entre desenvolvimento e produção

Mantenha o desenvolvimento, homologação e produção o mais similares possível

Quem nunca pelo menos ouviu a frase “Mas na minha máquina funcionou”?. Esse fator está relacionado a essa questão. Por muito tempo era difícil reduzir a lacuna entre ambientes, no entanto, há uma série de processos e tecnologias capazes de reduzir essa lacuna.

Por meio de conteinerização tornou-se muito fácil manter as aplicações funcionando em diferentes ambientes. Serviços de apoio, como banco de dados, também podem ser executados dessa forma. A partir de integração contínua é possível reduzir o tempo entre deploys em ambientes. Por meio de infraestrutura como código é possível versionar e replicar inclusive a infraestrutura da aplicação.

11. Logs

Trate logs como fluxos de eventos

Logs são fundamentais para a observabilidade de uma aplicação. Eles fornecem meios para saber o que ocorre com uma aplicação enquanto ela está em execução.

Muitas vezes, aplicações armazenam esses logs em arquivos. No entanto, uma aplicação 12 fatores não deve se preocupar em gerir os logs, afinal de contas, eles representam apenas fluxos de eventos que ocorrem com uma aplicação. Ao contrário, essas aplicações devem escrever o fluxo de eventos para a saída padrão stdout. Esse fluxo pode, posteriormente, ser enviado para um arquivo ou sistema indexador como Graylog, Splunk e Grafana.

12. Processos administrativos

Rode tarefas de administração/gestão em processos pontuais

Uma vez que podemos escalar aplicações em inúmeros servidores, as tarefas administrativas como a execução de scripts em banco de dados devem também ser executadas como processos isolados e automatizados.

Referências