Por que escolhemos WebAssembly em vez de APIs na nuvem
Por que escolhemos WebAssembly em vez de APIs na nuvem
Tarde de uma noite, nosso time estava encarando uma planilha de contas de nuvem e histogramas de latência. Cada pico de tráfego significava mais uma conversa sobre autoscaling, cold starts e se realmente precisávamos atravessar oceanos com PDFs só para juntar duas páginas. Naquela noite fizemos uma pergunta simples: e se o navegador pudesse fazer o trabalho pesado em vez disso?
O momento que mudou nossa arquitetura
O Ai2Done nasceu da crença de que ferramentas de produtividade não deveriam exigir que você confiasse num servidor de terceiros os seus contratos, faturas ou fotos de família. APIs na nuvem são poderosas, mas introduzem uma dependência permanente: seus bytes deixam sua máquina, atravessam redes que você não controla e pousam em discos que você nunca vai auditar. Para muitos fluxos isso é aceitável; para os nossos usuários era inaceitável.
WebAssembly nos deu um caminho crível para rodar código real — compilado de Go, a mesma linguagem que usamos no servidor — dentro da aba do usuário. Em vez de esboçar um cliente magro que sobe arquivos para uma caixa preta, podíamos entregar um binário portátil que executa localmente, com desempenho previsível e sem taxas-surpresa de saída de dados.
Por que não “só” JavaScript?
Amamos JavaScript para UI e orquestração, mas mover megabytes de lógica de PDF ou criptografia para JS escrito à mão teria sido mais lento de entregar e mais difícil de manter sincronizado com nossas ferramentas em Go. WASM nos deixa reaproveitar bibliotecas testadas em batalha (pense em pipelines no estilo do pdfcpu) e compartilhar tipos e testes pela stack. O navegador vira host, não alvo de reescrita.
Uma ponte mínima em JS carrega o módulo .wasm, passa handles de arquivo e callbacks de progresso e renderiza os resultados. O resto fica em Go:
// Forma conceitual: WASM expõe uma API fina; o trabalho real fica em internal/tools.
func ProcessPDF(input []byte) ([]byte, error) {
// merge, split, encrypt — tudo determinístico, tudo local
return merged, nil
}
O que ganhamos
Privacidade por construção. Quando o processamento acontece em WASM, não precisamos de uma política de privacidade que diga “podemos processar seus arquivos” — podemos dizer “não podemos”, porque os bytes nunca deixam o dispositivo. Isso não é marketing; é física.
Latência onde importa. Para operações em lote, o round-trip de rede frequentemente domina. WASM local elimina essa variável. As barras de progresso refletem trabalho real de CPU, não profundidade de fila na região de outra pessoa.
Previsibilidade de custo. Nossa conta de infraestrutura escala com tráfego ao site, não com gigabytes de documentos de usuário. Esse alinhamento nos mantém focados em qualidade do produto em vez de medir cada conversão.
Trade-offs que aceitamos honestamente
WASM não é grátis. O tamanho do download inicial importa, então comprimimos com Brotli, fazemos lazy-load por ferramenta e definimos limites sensatos de arquivo para que os usuários recebam mensagens amigáveis em vez de crashes de aba. Tetos de memória no navegador são reais; documentamos isso e mostramos progresso para que os usuários saibam que o app está trabalhando, não congelado.
Ainda usamos o servidor para coisas que pertencem lá: autenticação, indexação de busca e entrega de ativos estáticos. A divisão é intencional: servidor para identidade e descoberta, cliente para transformação.
Olhando à frente
Escolher WASM em vez de APIs na nuvem não foi uma aposta contra a nuvem — foi uma aposta na soberania do usuário. Toda vez que entregamos uma nova ferramenta, perguntamos se ela pode rodar localmente primeiro. Essa disciplina mantém o Ai2Done rápido, honesto e alinhado com as pessoas que dependem dele todos os dias. Se você está construindo nesta área, esperamos que nosso caminho lhe dê permissão para questionar o pipeline padrão “suba para processar” — e para deixar o navegador fazer mais do que costuma receber crédito.