Há alguns dias eu comecei uma brincadeira nova — e enorme — que eu nunca tinha tentado antes: criar uma linguagem de programação do zero.
A motivação é simples: linguagens populares foram desenhadas para humanos. E humanos gostam (ou toleram) ambiguidade. Só que LLMs sofrem com isso. any, null, coerções implícitas, sobrecarga confusa, padrões opcionais… esse “caos aceitável” vira um gerador de bugs quando você coloca um modelo no volante.
Então a ideia é construir uma linguagem pensada para reduzir ambiguidade e forçar escolhas que geram código previsível: tipagem obrigatória, nada de any, nada de null como padrão, e um modelo de memória que não dependa de “mágica” em runtime.
Eu ainda estou estruturando a linguagem, então hoje não é um post sobre sintaxe, parser, IR e afins. O que eu quero registrar aqui é um caso interessante que aconteceu esta semana.
Eu pedi para a IA montar um benchmark simples comparando um esqueleto de servidor HTTP entre minha linguagem, Rust e C++. Nada completo, nada “produção”: só um teste artificial para ter noção de throughput.
E o resultado foi inesperado: a minha linguagem ficou muito próxima de C++ e, naquele cenário específico, ficou acima do Rust.
Isso acende um alerta imediato: ou tem um truque real ali… ou tem um viés escondido no teste (e quase sempre tem). Então eu fui cavar o motivo.
Linguagem não é “texto”: é uma esteira até virar máquina
O que chamamos de “linguagem” é, na prática, um conjunto de regras (sintaxe + semântica) que alimenta um compilador (ou JIT). O computador não entende “if”, “class” ou “for”. Ele entende instrução de máquina.
No meu caso eu optei por usar LLVM como backend. Isso me dá:
um IR intermediário muito bem otimizado,
suporte a múltiplas plataformas,
e um caminho prático para interoperar com bibliotecas C/C++ via ABI/FFI.
LLVM não é “mágica”: ele não resolve tudo. Linkagem ainda pode ser um gargalo, e bootstrap de toolchain é um trabalho à parte. Mas como base para performance e portabilidade, é um atalho extremamente eficiente.
O benchmark não provou “minha linguagem é melhor”. Provou outra coisa.
O teste de HTTP foi uma simulação simples para medir requisições por segundo. E esse tipo de benchmark é uma mina de armadilhas:
qual biblioteca HTTP foi usada?
async ou sync?
qual allocator?
como o JSON/string foi tratado?
quantas alocações por request?
tem logging? tem formatação? tem cópia de buffer?
Ou seja: você não está medindo “HTTP”. Você está medindo alocação + cópia + layout de dados + modelo de concorrência.
E foi aí que apareceu o motivo real do resultado.
O vilão: alocação e cópia de string (e o herói: string view)
Eu escolhi não ter Garbage Collection. Não porque “GC é ruim” (essa frase é infantil), mas porque GC tem custo e pode introduzir latência imprevisível. Em sistemas de alta carga, você paga em throughput e/ou em picos de latência — dependendo do runtime e do padrão de alocação.
Então eu segui uma linha mais próxima de Rust/C++ moderno:
dados imutáveis onde faz sentido,
ownership explícito,
e estratégia agressiva para evitar alocações desnecessárias.
Só que a implementação inicial de Str (gerada pela IA) caiu numa escolha que foi a “chave” do benchmark: ela não tratava string como “um objeto dono de memória” em toda passagem de função. Ela tratava como uma visão (view) sobre um buffer.
Na prática, ao invés de “copiar string”, o código passava algo como:
ponteiro para bytes + tamanho ( (e às vezes capacidade, dependendo do tipo),
sem alocar,
sem clonar,
sem duplicar buffer.
Isso muda tudo. Porque em um servidor HTTP, o que mais aparece é:
parse de header,
roteamento,
comparação de strings curtas,
slicing de buffers,
parsing leve de payload.
Se você copia string ou aloca string nova em cada etapa, você morre por mil cortes.
E aqui vai o ponto crucial: Rust também consegue ser zero-copy, mas só se você escrever desse jeito.
Se no benchmark o código Rust estava criandoString(owned) onde poderia usar&str(slice) ouBytes, você acabou comparando “zero-copy” vs “aloca e copia”.
Ou seja: o resultado não foi “minha linguagem venceu Rust”. Foi: o meu protótipo caiu, por acaso, numa estratégia de string mais eficiente do que a usada naquela implementação em Rust.
E isso é um insight excelente, porque expõe o que realmente manda na performance: layout de dados e política de alocação.
Um mito comum: “em JS todo número vira I64”
Não é bem assim.
Em JavaScript, números geralmente são IEEE-754 double. Engines como V8 usam representações otimizadas (ex.: inteiros pequenos tagged), mas o modelo de número é bem diferente de “I64 sempre”. O efeito prático, porém, continua verdadeiro: em linguagens dinâmicas você frequentemente perde controle fino de representação, e isso pode virar custo em parsing, alocação, boxing/unboxing e coleta.
O problema não é “ser double”. O problema é: o runtime precisa adivinhar e adaptar em tempo real, e isso vira custo.
O que isso significa no mundo real?
Se você está num cenário de tráfego alto, o custo dominante muitas vezes não é CPU “pura”. É:
alocação,
cópia,
cache miss,
lock contention,
pausas de runtime,
e overhead de abstração.
Quando você elimina alocações por request e adota um modelo “string view + buffers reutilizáveis”, você reduz:
pressão no heap,
picos de latência,
e custo por request.
A consequência é direta: mesma máquina, mais requisições, menos custo.
Mas eu não vou vender fantasia. Não existe multiplicação mágica tipo “20x mais barato” só porque sim. Isso depende de gargalo real, infra, IO, kernel, TLS, load balancer, banco, cache, formato de payload, etc.
O que dá para afirmar com convicção é:
em serviços CPU-bound e allocation-bound, escolhas de memória e string podem dobrar throughput com facilidade;
em serviços IO-bound, o ganho pode ser marginal, porque o gargalo está fora do processo;
e em produção, a diferença “real” costuma vir da soma de 20 microdecisões, não de uma só.
Minha conclusão (e minha provocação)
A maioria das pessoas escolhe linguagem como quem escolhe skin. Depois tenta “otimizar” na marra quando o sistema já está caro e lento.
Só que a linguagem define os limites do que vai ser fácil ou difícil: previsibilidade de performance, modelo de memória, ergonomia de concorrência, custo de abstração, tooling.
E agora com IA, isso fica ainda mais agressivo: se você quer IA gerando código confiável, você precisa reduzir ambiguidade no nível da linguagem, não só “pedir com jeitinho no prompt”.
A minha aposta é simples e impopular:
o futuro não é “IA programando em qualquer linguagem”.
é linguagens e toolchains desenhadas para IA produzir código correto por padrão.
E esse benchmark foi um lembrete prático do que realmente importa: custo de alocação e cópia — o resto é conversa bonita.

