Estado global no servidor faz uma requisição contaminar outra: variáveis compartilhadas, caches e objetos instanciados fora de escopo alteram resultados conforme a ordem e a concorrência das chamadas. A solução passa por funções autocontidas, request context e injeção de dependência.
Por que requisições HTTP devem ser stateless
Requisições HTTP são independentes por design, e o protocolo foi definido assim desde a especificação RFC 9110, publicada em 2022 como consolidação das regras originais. Cada requisição precisa carregar tudo o que o servidor precisa para executar a ação, incluindo a identificação de quem chama.
Na prática, isso significa que o servidor não lembra de você entre uma chamada e outra. Quando você navega no YouTube e clica em outro vídeo, cada requisição carrega um identificador, como um token ou cookie, provando que você é você. O servidor nunca assume que o "usuário atual" persiste em memória.
Guardar algo como currentUser = "galego" em uma variável do servidor quebra esse contrato. As requisições chegam em paralelo, e cada usuário precisa receber as recomendações dele, não as do último que fez login.
Onde o estado global aparece sem você notar
Estado global no servidor não é só a variável óbvia do usuário atual. Ele se esconde em lugares que parecem inofensivos, e reconhecer esses pontos é metade da solução.
Cache em memória
Instanciar um cache dentro do processo do servidor é o exemplo mais comum. Ferramentas como o Redis, um banco de dados em memória muito usado para cache, podem rodar embutidas, e um simples Map em JavaScript já cumpre o mesmo papel. Tudo que qualquer requisição escrever ali afeta todas as outras.
Módulos que viram singletons
Em JavaScript, uma variável no topo do arquivo, como port = 3000, se comporta como global: o módulo é um singleton e o valor é compartilhado pelo sistema inteiro. Se alguma requisição alterar essa variável, altera para todos.
Objetos compartilhados entre métodos
Uma instância de moeda ou de desconto criada fora da classe, ou em uma classe compartilhada, carrega o mesmo risco. Configurações exportadas de módulos também entram nessa conta: são estado global que qualquer parte do código pode mutar.
Side effects criam bugs que você não reproduz
Uma função confiável recebe tudo de que precisa como parâmetro e não altera nada fora dela. Quando ela depende de um desconto instanciado externamente e ainda por cima muta esse valor, ela viola as duas regras ao mesmo tempo: a entrada vem de fora e o efeito colateral vaza para o servidor.
O resultado é que calculatePrice(30) deixa de ter resposta determinística. O retorno depende do valor do desconto naquele instante, definido por outra requisição. Quando o bug estourar em produção, chamar a função com os mesmos parâmetros na sua máquina não reproduz nada, porque o estado externo não veio junto no log.
O cenário típico é pior do que parece. O erro só acontece quando o usuário é do tipo retail e o produto tem estoque zero, condições que moram no banco de dados. Sem logar esse contexto, você olha uma chamada com valor 30 e não tem como saber por que falhou. Esse é o custo real do estado global: reprodutibilidade destruída.
Não por acaso, linguagens como Python e TypeScript têm incorporado cada vez mais estilo funcional, que valoriza funções puras e sem efeitos colaterais. O paradigma existe exatamente para eliminar essa classe de problema.
Race conditions: o risco mais óbvio do estado compartilhado
Duas requisições paralelas lendo e escrevendo o mesmo estado criam race conditions. É a consequência mais direta do estado global no servidor e a mais fácil de explicar: se o request A altera o desconto enquanto o request B calcula o preço, o resultado de B depende de uma corrida que ninguém controla.
A mesma lógica vale para funções que acumulam valores em um subtotal externo em vez de receber a lista como parâmetro e reduzir sobre ela. O subtotal vira terreno disputado por todas as chamadas concorrentes, e cada requisição contamina a seguinte.
Soluções: estado local, request context e injeção de dependência
Existem três caminhos complementares para eliminar a contaminação entre requisições, e eles funcionam juntos.
Passe o estado explicitamente
Quando a função precisa do usuário, busque-o no banco e passe o objeto como parâmetro na chamada. Uma função puramente de código, com o mesmo input, deve retornar o mesmo output. Se o bug acontecer, você reconstrói o objeto de usuário e reproduz o problema. A única exceção honesta é o que toca banco de dados ou API externa, porque isso sempre interage com algo fora do seu controle.
Use request context
Muitos frameworks e code bases adotam o padrão de contexto de requisição: cada request agrupa seu próprio contexto, que você repassa para frente. Na hora de logar, debugar e reproduzir, entender o que aconteceu fica muito mais simples.
Injete as dependências
Um provedor de pagamento instanciado fora da função parece inofensivo, mas é estado externo invisível. Com injeção de dependência, você define uma interface e injeta o provedor ao construir a classe de checkout. Mesmo sem o contexto completo dentro da função, o estado da classe registra qual provedor estava ativo naquele momento.
Frameworks de request-response como Express no Node.js e FastAPI em Python induzem exatamente esse comportamento: ao seguir o tutorial, você dificilmente compartilha contexto entre requisições. O framework não garante que você não vai errar, mas torna o erro bem menos provável.
Quando estado global no servidor é aceitável
Nem todo estado global é vilão. Uma constante de porta, como port = 3000, que ninguém altera em runtime, não causa problema real. O mesmo vale para um limite como maxUploadSize = 10 GB: se o valor nunca muda depois de definido, o risco de contaminação simplesmente não existe.
Configurações exportadas de módulos também costumam ser aceitáveis, desde que sejam somente leitura na prática. O único perigo é outro ponto do código mutar esses valores; se isso acontecer no seu time, uma alternativa é carregar a configuração uma única vez no boot do servidor e transformá-la em um objeto read-only. É defesa extra, e na maioria dos projetos não é necessária.
Perguntas frequentes sobre estado global
- O que é estado global no servidor? É qualquer dado compartilhado por todas as requisições do processo: variáveis de módulo, caches em memória, objetos instanciados fora de escopo e configurações mutáveis. Se um request altera o valor, todos os demais enxergam a mudança.
- Por que estado global causa bugs difíceis de reproduzir? Porque o resultado de uma função passa a depender do estado externo no momento da chamada, e não apenas dos parâmetros. Sem logar esse estado, chamar a função com os mesmos inputs localmente não reproduz o erro de produção.
- Cache em memória é sempre estado global ruim? Não. Cache é útil, mas é compartilhado por definição. O problema aparece quando requisições escrevem nele valores que afetam a lógica de outras requisições, ou quando a ordem de escrita altera resultados.
- Frameworks modernos resolvem isso sozinhos? Eles induzem o caminho certo: Express, FastAPI e similares entregam um contexto por requisição e desencorajam compartilhamento. Mas nada impede você de criar variáveis globais no seu código por cima do framework.
- Injeção de dependência elimina o estado externo? Ela o torna visível e controlável. A dependência continua existindo, mas fica registrada no construtor da classe, então você consegue saber qual implementação estava ativa quando o bug ocorreu.
Do vídeo ao artigo: transforme suas explicações em conteúdo
A ideia central aqui é que bugs sutis nascem de contexto invisível: o que a função não mostra nos parâmetros é justamente o que impede você de reproduzir o erro. Esse tipo de insight costuma ficar preso em vídeos longos, como as aulas do canal do Augusto Galego, e muito do que você explica ao vivo merecia uma versão escrita, pesquisável e bem estruturada.
Se você ensina programação, system design ou qualquer tema técnico em vídeo, o Skala Blog transforma essa gravação em artigo: você cola a URL do YouTube, o conteúdo é transcrito e vira um texto organizado, pronto para revisar e publicar.
Materiais como o curso de system design do Crazystack mostram como o mesmo assunto rende formatos diferentes — aula em vídeo para quem acompanha, artigo para quem busca a resposta pontual. Cabe a você escolher onde cada explicação vive.
Fork this article
Start a new branch from the same video, shaped your way. You keep the credit; the original keeps the attribution.
A fork in another language is filed as a translation of this article, so the two pages point at each other. You can unlink it later from the editor.
0/240
You are creating
- Format
- For
- Language
- Source
- Your angle
You will be asked to sign in before it is generated.
Buy credits