Domando cold starts em serverless: onde o tempo realmente vai
Um mergulho prático em cold starts — o que os causa, como medir o impacto real e quais otimizações valem o esforço.
"Serverless é lento por causa de cold start" é meio verdade e meio mito. O cold start existe, mas seu impacto depende muito de onde o tempo é gasto — e boa parte dele está sob seu controle.
Anatomia de um cold start
Um cold start típico tem três fases distintas:
- Provisionamento do sandbox — a plataforma aloca o ambiente. Você não controla isso, mas costuma ser a menor parte.
- Inicialização do runtime — carregar Node/Python/JVM e suas dependências.
- Inicialização do seu código — imports, conexões, leitura de config.
Na prática, a fase 3 é onde a maioria dos times perde mais tempo — e é justamente a mais fácil de otimizar.
Meça antes de otimizar
Antes de qualquer mudança, separe cold de warm nas métricas. Um p99 inflado por 5% de cold starts pede uma solução diferente de um p50 ruim em todas as requisições.
const COLD = !globalThis.__warmed;
globalThis.__warmed = true;
export async function handler(event) {
const start = performance.now();
const result = await process(event);
metrics.record("handler.duration", performance.now() - start, {
type: COLD ? "cold" : "warm",
});
return result;
}Otimizações que valem o esforço
- Enxugar o bundle. Cada MB de dependências é tempo de parsing. Tree-shaking e imports diretos cortam segundos.
- Lazy loading de dependências pesadas. O SDK que só é usado em 1 rota não precisa carregar nas outras.
- Mover trabalho para fora do hot path. Conexões e clients podem ser inicializados sob demanda, não no topo do módulo.
// ❌ Carrega o SDK em todo cold start, mesmo sem usar
import { S3 } from "@aws-sdk/client-s3";
const s3 = new S3();
// ✅ Só paga o custo quando realmente precisa
let _s3: import("@aws-sdk/client-s3").S3 | undefined;
async function getS3() {
if (!_s3) {
const { S3 } = await import("@aws-sdk/client-s3");
_s3 = new S3();
}
return _s3;
}Otimizações que raramente compensam
- Provisioned concurrency em tudo. Resolve cold start, mas reintroduz o custo fixo que você foi buscar no serverless. Use cirurgicamente, só em rotas sensíveis a latência.
- Reescrever em outra linguagem. O ganho de runtime raramente supera o custo de reescrita, a menos que você esteja em uma JVM em path crítico.
Conclusão
Cold start é um problema de distribuição, não de média. Meça separando cold de warm, ataque primeiro a inicialização do seu próprio código — que é barata de arrumar — e reserve soluções caras como provisioned concurrency para as poucas rotas onde a latência de cauda realmente importa para o usuário.