← Caderno de laboratório

Cada merge é um lançamento

Em 25 de outubro, uma mudança no Seat Time, nosso simulador de direção no navegador, não chegou à produção. Ou seja, não chegou ao jogo no ar. Nós o construímos com agentes de programação por inteligência artificial (IA). O merge, a integração das mudanças em main, nosso ramo compartilhado de lançamento, deveria publicá-las. Mas o build anterior, a versão compilada do jogo, continuou no ar. O registro do incidente resumiu: “Ninguém foi avisado; o build só ficou vermelho.”

O Seat Time já tinha recordes de volta, fantasmas e rodadas de competição. Mudávamos a física a cada poucos dias, então precisávamos saber qual build estava no ar. Queríamos que o merge concluísse o trabalho, sem outra rodada de ativação de recursos. Aquela tarde mostrou quanto ainda podia acontecer entre o merge e o navegador.

Anteriormente: O nome ficou pequeno.

Retiramos as flags de lançamento

Em 18 de outubro, removemos 23 flags de lançamento. Esses interruptores podiam deixar recursos já integrados esperando uma ativação separada. A regra passou a ser: um merge em main publica a mudança. Recursos ligados a dinheiro mantiveram seus próprios interruptores, desligados por padrão.

Essa decisão também tornou os valores gerados parte do lançamento. O build inclui um resumo de integridade: uma impressão digital do código de um componente do servidor. Esse componente reproduz os percursos gravados das atividades de bicos dentro do jogo. Ele se recusa a iniciar se a impressão digital não corresponder à aceita. Integrar código, portanto, exige conferir se os arquivos gerados continuam de acordo com ele.

Cada volta ganhou uma versão da física

Começamos a separar as voltas por versão da física em 12 de outubro, pela versão 2. Voltas, fantasmas e entradas do ranking receberam essa identificação. Os registros antigos continuaram disponíveis em um grupo separado. Eles deixaram de competir com voltas feitas sob outra física.

Nossa regra era incrementar a versão sempre que uma mudança na simulação alterasse os tempos de volta padrão. Sem isso, uma volta mais rápida poderia vir de um carro diferente, e não de uma pilotagem melhor. A identificação preservava os resultados antigos sem fingir que as condições continuavam iguais. Cada lançamento de física passou a carregar uma responsabilidade além do código novo.

Precisávamos de uma página que dissesse o que estava no ar

Pedimos aos agentes que adicionassem /version para informar a identidade do build em execução. Se essa identidade estivesse indisponível, a página retornaria uma resposta de indisponibilidade, sem inventar uma alternativa. Um merge bem-sucedido já não serviria como prova sobre a produção. Poderíamos perguntar ao próprio site no ar.

Também adicionamos uma jornada de smoke: um percurso automatizado curto pelos controles essenciais do jogo. Um conjunto anterior de mudanças compilou, mas quebrou três botões. Configurações não abria, o ranking ficava em “Carregando…” e Correr contra fantasma abria Configurações. Trechos de um patch caíram nos métodos errados; por isso, nossa verificação de lançamento aciona os controles.

Um build antigo substituiu um mais novo

Os builds podiam terminar fora de ordem, e vimos um antigo substituir um mais novo. Ter vários caminhos até a produção dificultava entender o problema. O publicador precisava comparar a ordem do código, sem presumir que o último upload fosse o mais recente. A proteção final verifica isso antes e depois de colocar o candidato no ar.

Nosso smoke de produção também falhava de forma enganosa. A verificação anterior nunca passava porque o renderizador automatizado avançava a simulação muito mais devagar que o tempo real. Mudamos a avaliação para acompanhar o progresso da simulação, em vez do tempo de relógio. Ela passou duas vezes em produção; depois, as esperas também passaram a observar progresso significativo do jogo.

Um publicador, com verificações depois da promoção

Reduzimos a publicação a um publicador do Cloudflare Workers Builds, que compila main. Ele prepara um candidato sem enviar tráfego para ele e verifica o tempo de processador na inicialização. Escolhemos um teto de 400 ms. Um candidato mais lento, ou sem medição, para antes de qualquer mudança no banco.

A sequência seguinte é deliberada: conferir a ordem, migrar o banco, confirmar a prontidão e promover o candidato. Promover significa colocá-lo no ar. Depois, o publicador verifica a identidade no ar, as respostas web e as jornadas no navegador. Uma falha nessas verificações aciona a reversão enquanto aquele candidato continua no ar. Essa reversão restaura o build anterior. Se um build mais novo assumiu, o publicador antigo recua em vez de desfazê-lo.

A publicação passa pelo limite de 400 ms de CPU na partida, migração e testes; falhas revertem a versão e builds antigos cedem aos mais novos.
Merge concluído, versão preparada e versão no ar passaram a ser fatos separados.

A promoção ainda vem antes da jornada no navegador, então esse mecanismo também cuida da recuperação. Não tratamos “enviado” como “aceito”. O build precisa chegar ao jogo em execução e exercitá-lo. Um lançamento pode ser recusado, revertido ou superado sem perder seu código no próximo build bem-sucedido.

O merge que não chegou à produção

Em 25 de outubro, clonar o repositório consumiu cerca de 8,5 minutos, em vez dos aproximadamente 3 minutos anteriores. A preparação do candidato terminou por volta de 12,5 minutos. O publicador informou 392 segundos restantes até seu prazo. A regra de admissão exigia uma reserva de 10 minutos e, por isso, recusou a publicação.

Nosso build tinha um limite de 20 minutos no Workers Builds. O publicador usa um prazo interno de 19 minutos para deixar margem antes desse corte. Os tempos registrados das etapas são aproximados; os 392 segundos informados pertencem ao prazo interno. Não são o resultado de subtrair o tempo arredondado de preparação dos 20 minutos.

Em 25 de outubro, o clone levou cerca de 8,5 minutos e a preparação terminou perto de 12,5 minutos. Os 392 segundos restantes não cobriam a reserva de dez minutos.
A reserva de segurança barrou a publicação; depois a reduzimos a seis minutos com base nos tempos medidos.

A recusa protegeu as verificações contra uma interrupção, mas também deixou o build anterior no ar. A falha apareceu no resultado do build sem chamar nossa atenção para o lançamento perdido. Quando a nova verificação mediu a diferença, a produção estava 30 minutos atrás de main. Nossa regra de lançamento era uma intenção que precisava de comprovação separada.

Tornamos a diferença visível, mas a recuperação ainda tinha um limite

Reduzimos a reserva de admissão para 6 minutos com base nos tempos observados. As jornadas de visitante, incluindo a preparação, tinham levado de 1,9 a 3,1 minutos. Mantivemos a verificação no navegador obrigatória, em vez de aceitar um lançamento por falta de tempo. Uma promoção lenta ainda poderia exigir uma reversão.

Os agentes adicionaram uma tentativa extra para um build iniciado por um push, o envio de mudanças ao repositório. Ela não pode se repetir indefinidamente nem duplicar um build existente. Também não pode ocorrer quando um código mais novo já está preparado. Em 28 de outubro, surgiu um limite prático: a tentativa automática foi recusada por falta de permissão. A recuperação registrada usou o painel; o mecanismo não comprovava uma recuperação sem intervenção.

O novo comando check-live compara main com o build em execução e alerta após mais de 25 minutos de atraso. Esse é nosso limite escolhido, não uma garantia medida de entrega. Na consulta de 6 de novembro, encontramos instruções para agendá-lo, mas nenhum agendamento versionado. Ainda precisamos confirmar um agendamento antes de chamar isso de monitoramento automático.

Agora informamos separadamente o que foi integrado, preparado e colocado no ar. As verificações locais devem terminar antes do merge, e não devemos juntar vários merges sem conferir a publicação. “Está no ar?” tem uma resposta direta no build em execução. “A mudança foi integrada” deixou de ser resposta suficiente.

Uma semana depois, o lançamento chegou ao ambiente de testes

Na semana seguinte, até 2 de novembro, as versões da física avançaram de v8 a v17. Cada incremento trouxe impressões digitais, voltas de referência, metas de medalhas e registros gerados para atualizar. Alguns lançamentos esperaram o encerramento de períodos de competição. Em 1º de novembro, escolhemos lançamentos contínuos: publicar as mudanças conforme ficassem prontas.

Essas mudanças podiam alterar resultados registrados. Na v16, a volta automatizada de smoke passou de 133,425 para 132,469 segundos. A pontuação registrada de Jump Line passou de 621, prata na v15, para 594, bronze. Nenhuma meta de medalha mudou na v17. Nenhum dos dois registros de lançamento incluía pilotagem pré-merge com G923 ou DualSense. Essas verificações não permitem descrever como era dirigir cada build no hardware.

O próprio ambiente de testes precisava de reparos. Descobrimos que as verificações locais de integração contínua tinham deixado de fora 575 dos 710 arquivos de teste do Seat Time. Essas verificações testam as mudanças conforme são integradas. Os arquivos ficaram fora daquele executor local; isso não significa que ninguém jamais os tivesse executado. Adicionamos um grupo que captura os demais testes e uma proteção que exige exatamente um grupo por arquivo.

Depois, um espaço no nome da pasta do projeto quebrou os mocks do navegador no Vitest 4.1.10. Mocks são substitutos usados nos testes. A correção original para pastas com espaços explica: os caminhos codificados mantinham %20 onde a busca precisava de um espaço. Aplicamos um patch à versão da dependência que havíamos fixado. Em 6 de novembro, a correção estava integrada ao projeto original. Mas o parser da v4.1.12, que analisa rastros de erros, ainda não decodificava esses endereços.

Valores gerados causaram outra rodada de problemas ao serem refeitos sobre o estado errado do código. Nossa regra passou a ser regenerá-los sobre o código final integrado. Cada lançamento também recebe um nome correspondente à versão da física que realmente entrega. As duas regras ajudam a manter juntos o código, suas evidências e o nome informado.

A seguir, os números dentro do build

Agora conseguimos distinguir um merge de um lançamento no ar, mas ainda falta confirmar o agendamento do monitoramento. A lacuna de permissão da nova tentativa também impede afirmar que a recuperação automática está completa. Nossa interpretação desse período é simples: cada proteção tem um incidente por trás. Se toda volta precisa de uma versão da física, cada número dessa física precisa de uma fonte.

Dirija no Seat Time.

Próximo episódio: Cada número ganha uma fonte.

Notas e fontes

  • Fato documentado — nossos registros de lançamento: 23 flags removidas em 18 de outubro; interruptores financeiros continuaram desligados por padrão. Impressões digitais, /version, os três controles quebrados, a substituição por um build antigo e o publicador único descrevem nossa implementação. O smoke baseado no tempo da simulação passou duas vezes; isso não é uma taxa de confiabilidade.
  • Valor de projeto escolhido — nossas regras: a identificação da física começou na versão 2 em 12 de outubro. Voltas, fantasmas e rankings antigos ficam separados. O teto de inicialização é 400 ms; o prazo interno é 19 minutos. A reserva passou de 10 para 6 minutos, com uma tentativa extra condicional e alerta após mais de 25 minutos.
  • Medido — incidente de 25 de outubro: clone de aproximadamente 3 para 8,5 minutos; preparação em aproximadamente 12,5 minutos; 392 segundos restantes informados; produção 30 minutos atrasada na medição. As jornadas de visitante levaram de 1,9 a 3,1 minutos. São medições históricas, não novos benchmarks.
  • Fato documentado — limite da plataforma: Cloudflare, limites e preços do Workers Builds, informa um limite de 20 minutos por build. Consultado em 6 de novembro de 2026. Os demais limites acima são nossos.
  • Fato documentado — limites operacionais restantes: o registro de 28 de outubro diz que faltou permissão para a nova tentativa; a recuperação usou o painel. A revisão de 6 de novembro não encontrou agendamento versionado para check-live. Isso não determina o que pode existir fora do repositório.
  • Fato documentado / valor de projeto escolhido — acontecimentos posteriores: v8–v17, esperas por períodos de competição, a decisão de lançamentos contínuos de 1º de novembro, nome único e regeneração após o merge final vêm dos nossos registros.
  • Medido / ainda não pilotado no hardware: volta de smoke da v16 de 133,425 para 132,469 segundos; pontuação registrada de Jump Line de 621 para 594, prata para bronze. A v17 não alterou metas de medalhas. Os registros não contêm pilotagem pré-merge com G923 ou DualSense; essas verificações não permitem descrever a sensação ao dirigir.
  • Medido / fato documentado — testes: o executor local omitiu 575 de 710 arquivos. A falha registrada nos mocks ocorreu no Vitest 4.1.10. A correção para espaços foi integrada em 9 de outubro de 2026. Em 6 de novembro, lemos as notas da v4.1.11, as notas da v4.1.12 e o código do parser da v4.1.12. Ele ainda não incluía a decodificação; não afirmamos que nosso patch local possa ser removido.
  • Nossa interpretação: essas falhas tornaram a identidade do lançamento e suas evidências parte da publicação. Agentes de programação por IA ajudaram na implementação; nós tomamos as decisões de lançamento. Este artigo usou IA na redação e na tradução. Os nomes de produtos e ferramentas são descritivos, não endossos.

Continue lendo