UV vs PIP: Acelerando o Build Python

O Problema da Velocidade de Build

Um dos desafios que impactava significativamente nossa produtividade era o tempo de build durante o desenvolvimento com Docker. O processo tradicional de instalação de dependências Python usando pip estava consumindo aproximadamente 3 minutos para cada build completo.

graph LR
    subgraph "Comparação de Performance"
        P["pip<br>~3 minutos"] --> C{Comparação}
        U["uv<br>~30 segundos"] --> C
    end
    
    subgraph "Impacto no Desenvolvimento"
        C --> D["Desenvolvimento<br>Fragmentado"]
        C --> I["Desenvolvimento<br>Integrado"]
        
        D --> DL["Python Local"]
        D --> DB["DB Separado"]
        D --> DF["Frontend Separado"]
        
        I --> ID["Docker Completo"]
        I --> IH["Hot Reload"]
        I --> IC["CI/CD Rápido"]
    end
    
    classDef pip fill:#ff9999,stroke:#333,stroke-width:2px
    classDef uv fill:#99ff99,stroke:#333,stroke-width:2px
    classDef fragmented fill:#ffcccc,stroke:#333,stroke-width:2px
    classDef integrated fill:#ccffcc,stroke:#333,stroke-width:2px
    
    class P pip
    class U uv
    class D,DL,DB,DF fragmented
    class I,ID,IH,IC integrated

Este tempo excessivo de build tinha consequências diretas:

  • Queda na produtividade: desenvolvedores precisavam esperar longos períodos após cada alteração
  • Desenvolvimento fragmentado: muitos optavam por desenvolver com Python local, banco de dados e frontend separados
  • Complexidade adicional: manter ambientes separados gerava inconsistências e bugs difíceis de rastrear
  • Dificuldade para novos contribuidores: o processo de setup era complicado e demorado

Descobrindo o UV: Um Compilador Python em Rust

Durante nossa busca por soluções, descobrimos o UV, um instalador e resolvedor de dependências Python desenvolvido em Rust pela comunidade open-source.

O que torna o UV especial?

  • Desenvolvido em Rust: linguagem conhecida por sua performance e segurança
  • Paralelismo eficiente: instala e resolve dependências simultaneamente
  • Cache inteligente: reutiliza pacotes já baixados de forma eficiente
  • Compatibilidade: funciona como substituto direto do pip
  • Open-source: mantido ativamente pela comunidade

A Transformação: De 3 Minutos para 30 Segundos

A substituição do pip pelo uv em nosso processo de build resultou em uma redução drástica no tempo de build:

FerramentaTempo Médio de BuildImpacto na Produtividade
pip~3 minutosDesenvolvimento fragmentado
uv~30 segundosFluxo de trabalho integrado

Esta melhoria de desempenho de aproximadamente 6x transformou completamente nosso fluxo de desenvolvimento.

Implementação no Dockerfile

A implementação foi surpreendentemente simples. Substituímos o pip pelo uv em nosso Dockerfile de desenvolvimento:

# Antes (com pip)
FROM python:3.10-slim as dev
WORKDIR /app
COPY requirements-dev.txt .
RUN pip install --no-cache-dir -r requirements-dev.txt

# Depois (com uv)
FROM python:3.10-slim as dev
WORKDIR /app
COPY requirements-dev.txt .
RUN pip install --no-cache-dir uv
RUN uv pip install --no-cache-dir -r requirements-dev.txt

Benefícios Além da Velocidade

A adoção do uv trouxe benefícios adicionais além da simples redução no tempo de build:

  1. Ambiente de desenvolvimento unificado: todos os desenvolvedores passaram a usar o mesmo ambiente Docker
  2. Facilidade para novos contribuidores: setup muito mais rápido e simples
  3. Desenvolvimento hot-reload: viabilizou a implementação de hot-reload no Docker
  4. Resolução mais confiável de dependências: menos conflitos e problemas de compatibilidade
  5. Melhor utilização de recursos: builds mais eficientes em termos de CPU e memória

Por Que Dois Dockerfiles?

Uma questão que surgiu foi: "Por que manter dois Dockerfiles separados (dev e prod) em vez de unificá-los?"

Nossa decisão foi baseada em várias considerações:

  1. Otimização para diferentes casos de uso:

    • Dockerfile de desenvolvimento: prioriza velocidade de build e facilidade de desenvolvimento
    • Dockerfile de produção: prioriza segurança, tamanho da imagem e estabilidade
  2. Ferramentas específicas de desenvolvimento:

    • O ambiente de desenvolvimento inclui ferramentas como debuggers e hot-reload
    • O ambiente de produção é mais enxuto e otimizado
  3. Facilidade para contribuidores:

    • Desenvolvedores podem iniciar o ambiente completo com um único comando
    • Não é necessário conhecimento profundo de infraestrutura, CI/CD ou redes

A separação nos permitiu otimizar cada ambiente para seu propósito específico, tornando o projeto mais acessível para novos contribuidores.

Lições Aprendidas

Esta experiência nos ensinou valiosas lições:

  1. Ferramentas importam: a escolha das ferramentas certas pode transformar drasticamente a produtividade
  2. Rust para performance: linguagens de sistemas como Rust podem trazer benefícios significativos para ferramentas de desenvolvimento
  3. Experimentação vale a pena: testar novas abordagens, mesmo que não convencionais, pode levar a grandes melhorias
  4. Comunidade open-source: aproveitar o trabalho da comunidade pode resolver problemas que parecem intratáveis

Impacto no Projeto como um Todo

A adoção do uv foi um dos fatores que mais contribuíram para a produtividade e satisfação dos desenvolvedores no projeto. O que começou como uma otimização técnica acabou transformando fundamentalmente nossa forma de trabalhar e colaborar.


"Tempo é o recurso mais valioso que temos; que bom que existe uma forma de compilar Python em 30 segundos ao invés de 3 minutos."