Olá, amigos. Na véspera do lançamento do curso , tradicionalmente compartilhamos com vocês a tradução de um material útil.
O software está resolvendo cada vez mais tarefas do dia a dia, enquanto se torna cada vez mais complexo. Como disse uma vez Marc Andreessen, ele está consumindo o mundo.

Como resultado, nos últimos anos, as abordagens para desenvolvimento e entrega de aplicativos mudaram drasticamente. Foram mudanças de escala tectônica que levaram ao surgimento de um conjunto de princípios. Esses princípios se mostraram úteis na formação de equipes, no design, desenvolvimento e entrega de seu aplicativo para os usuários finais.
Os princípios podem ser resumidos da seguinte forma: o aplicativo deve ser pequeno, em rede e ter uma arquitetura orientada ao desenvolvedor. Com base nesses três princípios, você pode criar um aplicativo robusto e complexo que pode ser entregue rapidamente e de forma segura ao usuário final, além de ser facilmente escalável e expansível.

Cada um dos princípios propostos possui uma série de aspectos que discutiremos para mostrar como cada princípio contribui para alcançar o objetivo final, que é a entrega rápida de aplicativos confiáveis, fáceis de manter e usar. Vamos considerar os princípios em comparação com seus opostos, para esclarecer o que significa, por exemplo, 'Certifique-se de usar o princípio da pequena dimensão».
Esperamos que este artigo incentive você a utilizar os princípios propostos para construir aplicativos modernos, que garantirão uma abordagem única ao design no contexto de uma pilha de tecnologias em constante crescimento.
Ao aplicar esses princípios, você descobrirá que está aproveitando as últimas tendências em desenvolvimento de software, incluindo a abordagem para desenvolvimento e entrega de aplicativos, o uso de contêineres (por exemplo, ) e frameworks para orquestração de contêineres (por exemplo, ), o uso de microsserviços (incluindo Arquitetura de Microsserviços e para aplicativos de microsserviços.
O que é um aplicativo moderno?
Aplicativos modernos? Pilha moderna? O que exatamente significa 'moderno'?
A maioria dos desenvolvedores tem apenas uma ideia geral do que constitui um aplicativo moderno, portanto é necessário fornecer uma definição clara desse conceito.
Um aplicativo moderno suporta vários clientes, seja uma interface de usuário em uma biblioteca JavaScript React, um aplicativo móvel para Android ou iOS, ou um aplicativo que se conecta a outro por meio de uma API. Um aplicativo moderno implica a existência de um número indeterminado de clientes para os quais ele fornece dados ou serviços.
Um aplicativo moderno fornece uma API para acessar os dados e serviços solicitados. A API deve ser imutável e consistente, e não escrita especificamente para uma solicitação específica de um determinado cliente. A API está disponível via HTTP(S) e fornece acesso a todas as funcionalidades disponíveis na GUI ou CLI.
Os dados devem estar disponíveis em um formato comum e compatível, como JSON. A API fornece objetos e serviços de forma clara e organizada, por exemplo, APIs RESTful ou GraphQL oferecem uma interface adequada.
Os aplicativos modernos são construídos em uma pilha moderna, e uma pilha moderna é aquela que suporta esses aplicativos, respectivamente. Essa pilha permite que o desenvolvedor crie facilmente um aplicativo com interface HTTP e pontos finais de API bem definidos. A abordagem escolhida permitirá que seu aplicativo receba e envie dados no formato JSON com facilidade. Em outras palavras, uma pilha moderna corresponde aos elementos do Aplicativo de Doze Fatores. .
As versões populares desse tipo de pilha são baseadas em , , , , e . A Arquitetura de Microserviços representa um exemplo de uma pilha moderna implementada em cada uma das linguagens mencionadas.
Observe que não estamos promovendo exclusivamente a abordagem de microserviços. Muitos de vocês trabalham com monólitos que precisam evoluir, enquanto outros lidam com aplicativos SOA que estão se expandindo e evoluindo para se tornarem aplicativos de microserviços. Outros ainda estão se movendo em direção à aplicação de aplicativos sem servidor (serverless), e alguns estão implementando combinações dos mencionados acima. Os princípios expostos neste artigo se aplicam a cada um desses sistemas, apenas com algumas pequenas alterações.
Princípios
Agora que chegamos a um entendimento comum sobre o que são aplicações modernas e uma pilha moderna, é hora de mergulhar nos princípios de arquitetura e desenvolvimento que serão de grande ajuda na criação, implementação e manutenção de uma aplicação moderna.
Um dos princípios é "criar aplicações pequenas", vamos apenas chamá-lo de princípio da pequenez. Existem aplicações incrivelmente complexas que consistem em uma grande quantidade de componentes móveis. Por sua vez, construir uma aplicação a partir de pequenos componentes discretos simplifica seu design, manutenção e operação como um todo. (Note que dissemos "simplifica", e não "torna simples").
O segundo princípio é que podemos aumentar a produtividade dos desenvolvedores, ajudando-os a se concentrar nas funcionalidades que estão desenvolvendo, ao mesmo tempo em que os liberamos das preocupações com a infraestrutura e CI/CD durante a implementação. Então, em poucas palavras, nossa abordagem é centrada no desenvolvedor.
Por fim, tudo o que diz respeito ao seu aplicativo deve estar conectado à rede. Nas últimas duas décadas, avançamos muito em direção a um futuro conectado, uma vez que as redes se tornaram mais rápidas e as aplicações mais complexas. Como já mencionado, uma aplicação moderna deve ser usada pela rede por uma variedade de clientes diferentes. Aplicar o pensamento em rede na arquitetura traz benefícios significativos que se alinham bem com princípio da pequenez e o conceito de abordagem centrada no desenvolvedor.
Se, ao desenvolver e implementar sua aplicação, você se lembrar desses princípios, terá uma vantagem inegável no crescimento e na entrega do seu produto.
Vamos explorar esses três princípios em mais detalhes.
Princípio da Pequenez
Para o cérebro humano, é difícil processar uma grande quantidade de informações ao mesmo tempo. Na psicologia, o termo carga cognitiva refere-se à quantidade total de esforço mental necessário para reter informações na memória. Reduzir a carga cognitiva dos desenvolvedores é uma prioridade, pois assim eles podem se concentrar em resolver o problema, em vez de manter na cabeça o atual modelo complexo de toda a aplicação e as funcionalidades em desenvolvimento.

As aplicações são decompostas pelas seguintes razões:
- Redução da carga cognitiva sobre os desenvolvedores;
- Aceleração e simplificação dos testes;
- Entrega rápida de alterações na aplicação.
Existem várias maneiras de reduzir a carga cognitiva sobre os desenvolvedores, e é aqui que o princípio da simplicidade entra em jogo.
Assim, três maneiras de reduzir a carga cognitiva:
- Reduzir os prazos que eles devem considerar ao desenvolver uma nova funcionalidade – quanto mais curto o prazo, menor a carga cognitiva.
- Reduzir a quantidade de código em que se trabalha ao mesmo tempo – menos código significa menos carga.
- Simplificar o processo de realização de alterações incrementais na aplicação.
Redução dos prazos de desenvolvimento
Vamos voltar ao tempo em que a metodologia waterfall era o padrão para o processo de desenvolvimento, e prazos de seis meses a dois anos para desenvolver ou atualizar uma aplicação eram uma prática comum. Normalmente, os engenheiros liam documentos relevantes, como o documento de requisitos do produto (PRD), o documento de referência do sistema (SRD), o plano de arquitetura e começavam a juntar todas essas coisas em um único modelo cognitivo, de acordo com o qual escreviam o código. À medida que os requisitos e, consequentemente, a arquitetura mudavam, era necessário fazer um esforço significativo para informar toda a equipe sobre as atualizações no modelo cognitivo. Essa abordagem, na pior das hipóteses, poderia simplesmente paralisar o trabalho.
A maior mudança no processo de desenvolvimento de aplicações foi a implementação da metodologia ágil. Uma das principais características da metodologia agile é o desenvolvimento iterativo. Por sua vez, isso leva à redução da carga cognitiva sobre os engenheiros. Em vez de exigir que a equipe de desenvolvedores implemente a aplicação em um longo ciclo, agile a abordagem permite focar em pequenas quantidades de código que podem ser rapidamente testadas e implantadas, enquanto se obtém feedback. A carga cognitiva da aplicação deslocou-se de prazos de seis meses a dois anos, com um enorme número de especificações, para adições ou alterações de funcionalidades de duas semanas, orientadas por uma compreensão mais difusa de uma grande aplicação.
A mudança do foco de um aplicativo massivo para funções específicas e pequenas, que podem ser concluídas em um sprint de duas semanas, tendo em mente não mais que uma função do próximo sprint, representa uma alteração significativa. Isso aumentou a produtividade do desenvolvimento, ao mesmo tempo que reduziu a carga cognitiva, que estava sempre oscilando.
Na metodologia agile supõe-se que o aplicativo final será uma versão levemente modificada do conceito original, portanto, o ponto final do desenvolvimento é necessariamente ambíguo. Somente os resultados de cada sprint específico podem ser claros e precisos.
Bases de código pequenas
O próximo passo para reduzir a carga cognitiva é diminuir a base de código. Em geral, aplicações modernas são massivas – um aplicativo corporativo confiável pode consistir em milhares de arquivos e centenas de milhares de linhas de código. Dependendo da organização dos arquivos, as relações e dependências entre o código e os arquivos podem ser óbvias ou, ao contrário, difíceis de discernir. Até mesmo a depuração da execução do código pode apresentar problemas, dependendo das bibliotecas utilizadas e de quão bem as ferramentas de depuração segregam bibliotecas/pacotes/módulos e código do usuário.
Construir um modelo mental funcional do código de um aplicativo pode levar um tempo considerável, e novamente impor ao desenvolvedor uma carga cognitiva maior. Isso é especialmente característico de bases de código monolíticas, onde há uma grande quantidade de código, e a interação entre os componentes funcionais não é bem definida, enquanto a separação dos objetos de atenção é frequentemente nebulosa, devido à falta de limites funcionais.
Uma das maneiras eficazes de reduzir a carga cognitiva dos engenheiros é a transição para uma arquitetura de microsserviços. Na abordagem de microsserviços, cada serviço foca em um conjunto específico de funções; ao mesmo tempo, o significado do serviço geralmente é definido e compreendido. Os limites do serviço também são claros – lembre-se de que a comunicação com o serviço é feita através de APIs, portanto, os dados gerados por um serviço podem ser facilmente transferidos para outro.
A interação com outros serviços geralmente é limitada a alguns serviços do usuário e alguns serviços do provedor, que utilizam chamadas de API simples e claras, como por meio do REST. Isso significa que a carga cognitiva sobre o engenheiro é seriamente reduzida. A tarefa mais difícil permanece em entender o modelo de interação dos serviços e como coisas como transações ocorrem entre vários serviços. Em última análise, o uso de microsserviços reduz a carga cognitiva, diminuindo a quantidade de código, definindo limites claros para o serviço e garantindo compreensão das relações entre usuários e provedores.
Pequenas mudanças incrementais
O último elemento do princípio pequenez – é a gestão das mudanças. Um grande desafio para os desenvolvedores é olhar para a base de código (mesmo, possivelmente, para seu próprio código mais antigo) e afirmar: 'Isso é uma bagunça, precisamos reescrever tudo isso.' Às vezes, essa é a decisão correta, e às vezes não. Isso impõe à equipe de desenvolvedores o fardo de uma mudança global de modelo, o que, por sua vez, resulta em uma carga cognitiva em larga escala. É melhor que os engenheiros se foquem nas mudanças que podem fazer durante o sprint, para depois entregar a funcionalidade necessária a tempo, mesmo que de forma gradual. O produto final deve se parecer com o que foi planejado, mas com algumas mudanças e testes para atender às necessidades do cliente.
Ao reescrever grandes partes do código, às vezes é impossível entregar rapidamente a alteração, pois outras dependências do sistema entram em jogo. Para controlar o fluxo de mudanças, pode-se usar a ocultação de funcionalidades (feature hiding). Em essência, isso significa que a funcionalidade está na produção, mas não está acessível por meio de variáveis de ambiente (env-var) ou outro mecanismo de configuração. Se o código passou por todos os processos de verificação de qualidade, ele pode estar na produção em um estado oculto. No entanto, essa estratégia funciona apenas se a função for ativada no final. Do contrário, ela apenas bagunçará o código e adicionará uma carga cognitiva que o desenvolvedor terá que enfrentar para trabalhar de maneira produtiva. O gerenciamento de mudanças e as alterações incrementais ajudam a manter a carga cognitiva dos desenvolvedores em um nível manejável.
Os engenheiros enfrentam muitas dificuldades mesmo ao implementar funcionalidades adicionais simples. Do lado da gestão, é sensato reduzir a carga desnecessária na equipe para que ela possa se concentrar nos elementos principais da funcionalidade. Existem três coisas que você pode fazer para ajudar sua equipe de desenvolvedores:
- Usar uma metodologia
agile, para limitar o prazo em que a equipe deve se concentrar nas funcionalidades-chave. - Implementar seu aplicativo como vários microsserviços. Isso limitará o número de funcionalidades implementadas e fortalecerá as fronteiras que mantêm a carga cognitiva sob controle durante o trabalho.
- Prefira mudanças incrementais em vez de grandes e complexas, altere pequenos trechos de código. Aplique a ocultação de funcionalidades para implementar mudanças, mesmo que elas não sejam visíveis imediatamente após a adição.
Se você aplicar o princípio da simplicidade em seu trabalho, sua equipe se tornará muito mais feliz, terá um foco melhor na implementação das funções necessárias e terá maior probabilidade de lançar mudanças de qualidade mais rapidamente. Mas isso não significa que o trabalho não possa se tornar mais complicado; às vezes, implementar uma nova funcionalidade exige a modificação de vários serviços e esse processo pode ser mais complexo do que um semelhante em uma arquitetura monolítica. De qualquer forma, os benefícios de aplicar a abordagem da simplicidade valem a pena.
Fim da primeira parte.
Em breve, publicaremos a segunda parte da tradução, mas agora aguardamos seus comentários e convidamos você para , que acontecerá hoje às 20:00.
Fonte: habr.com
