A inteligência artificial está transformando a forma como desenvolvemos software. Hoje é possível gerar um CRUD completo em poucos minutos utilizando ferramentas como ChatGPT, Claude, Gemini e outras plataformas de IA.

Nesse contexto surge uma pergunta estratégica: qual stack tecnológica consome menos tokens e entrega maior produtividade na geração de código?

Essa questão importa porque o consumo de tokens impacta diretamente o custo de utilização da IA, a velocidade de geração, a clareza das respostas, a facilidade de manutenção e a quantidade de contexto disponível para regras de negócio.

O que são tokens

Tokens são as unidades de texto processadas pelos modelos de linguagem. Cada modelo possui um orçamento finito de tokens por requisição, e tudo que ele lê ou produz consome desse orçamento.

  • Palavras inteiras ou fragmentos de palavras.
  • Símbolos e pontuações.
  • Trechos de código fonte, com nomes de variáveis, tipos e estruturas.
  • Espaços, quebras de linha e indentação.

Quanto mais extenso e verboso for o código, maior será o consumo de tokens. Frameworks que exigem muito boilerplate ou decoradores extensos drenam o orçamento antes mesmo da regra de negócio aparecer.

Por que isso importa

Ao desenvolver aplicações com apoio de IA, parte do orçamento de tokens é consumida pela estrutura do próprio código. Stacks mais enxutas oferecem vantagens diretas para o time de engenharia.

  • Menor custo operacional em chamadas para a API do modelo.
  • Respostas mais rápidas, porque o contexto enviado é menor.
  • Menor necessidade de dividir prompts em várias etapas.
  • Mais espaço para descrever regras de negócio e requisitos específicos.
Quanto menos tokens a infraestrutura consome, mais tokens sobram para o que realmente gera valor para o negócio.

Metodologia do benchmark

Para tornar a comparação justa, definimos um cenário padrão que qualquer stack precisa entregar: um CRUD completo para a entidade Customer, com regras de negócio explícitas e endpoints REST padronizados.

A entidade Customer possui os seguintes campos:

  • id
  • name
  • email
  • document
  • phone
  • status
  • created_at
  • updated_at

Os endpoints REST esperados são:

  • POST /customers, cria um novo cliente.
  • GET /customers, lista clientes com paginação.
  • GET /customers/:id, recupera um cliente específico.
  • PUT /customers/:id, atualiza dados do cliente.
  • DELETE /customers/:id, remove o cliente.

As regras de negócio que cada implementação precisa garantir:

  • O campo name é obrigatório.
  • O email deve ser único entre os clientes.
  • O document deve ser único entre os clientes.
  • O status aceita apenas os valores active ou inactive.
  • A listagem retorna resultados paginados.
  • Erros são padronizados em respostas JSON.

Stacks avaliadas

Nove stacks foram colocadas no mesmo prompt, cobrindo desde ecossistemas onde o CRUD é o caso de uso central até linguagens cujo forte está em outro tipo de serviço. A diversidade era proposital: queríamos ver se stacks tradicionalmente associadas a CRUD de fato pagam dividendos quando a IA é quem está digitando.

  • PHP com Laravel.
  • Python com Django e Django REST Framework.
  • Bun com Elysia e Drizzle.
  • Bun com Hono e Drizzle.
  • TypeScript com NestJS e Prisma.
  • Java com Spring Boot e JPA.
  • C# com ASP.NET Core e Entity Framework Core.
  • Go com Fiber e Ent.
  • Go com Gin e GORM.

Os critérios deste benchmark se dividem em três grupos, separados por como cada um foi avaliado:

  • Medidos diretamente a partir da resposta do modelo: total de tokens consumidos na geração, linhas de código produzidas e quantidade de arquivos gerados.
  • Avaliados qualitativamente neste artigo: aderência do código ao funcionamento real, baseada em revisão linha a linha dos outputs para identificar quanto retrabalho cada stack pediria.
  • Reservados para o próximo ciclo: tempo de implementação até o primeiro deploy local e complexidade de manutenção a longo prazo, ambos exigindo execução do código e múltiplas iterações.

Resultado do benchmark

Rodamos o benchmark localmente com Qwen 2.5 Coder 14B via Ollama no MacBook Pro. Cada stack foi executada três vezes para neutralizar a variância natural do modelo, e o número reportado é a mediana de tokens de saída produzidos pela geração de código.

  • 1. Python com Django e Django REST Framework — 681 tokens, 92 linhas, 6 arquivos
  • 2. PHP com Laravel — 725 tokens, 138 linhas, 5 arquivos
  • 3. Bun com Elysia e Drizzle — 1.205 tokens, 145 linhas, 7 arquivos
  • 4. Go com Gin e GORM — 1.247 tokens, 186 linhas, 5 arquivos
  • 5. Go com Fiber e Ent — 1.317 tokens, 211 linhas, 5 arquivos
  • 6. TypeScript com NestJS e Prisma — 1.393 tokens, 209 linhas, 9 arquivos
  • 7. Java com Spring Boot e JPA — 1.514 tokens, 281 linhas, 7 arquivos
  • 8. Bun com Hono e Drizzle — 1.587 tokens, 200 linhas, 8 arquivos
  • 9. C# com ASP.NET Core e Entity Framework Core — 1.981 tokens, 341 linhas, 7 arquivos

Quatro conclusões saltam aos olhos. Primeiro, o pódio mostra que stacks onde CRUD é o caso de uso central, com convenção forte e geração implícita de rotas, gastam significativamente menos tokens. Django liderou com 681 tokens graças ao ModelViewSet do DRF, que substitui cinco rotas REST por uma classe. Laravel ficou em segundo, a apenas 44 tokens de distância, com Route::apiResource fazendo papel equivalente.

Segundo, a diferença entre o primeiro e o último foi de quase três vezes o consumo. C# com ASP.NET Core e Entity Framework Core terminou em nono com 1.981 tokens — fruto direto do estilo verboso da plataforma .NET, com DTOs separados, DataAnnotations explícitas, repositórios formalizados e Program.cs com configuração de builder pattern. Tudo isso é ergonomia para o time, mas custa caro quando a IA é quem está digitando.

Terceiro, as stacks Go ficaram na zona média do ranking, não no topo. Convenções explícitas, parsing manual e tratamento individual de erro em cada handler somam tokens. Go é uma escolha excelente para serviços de infraestrutura ou APIs de alta performance, mas o domínio típico do CRUD favorece linguagens com mais convenção.

Quarto, NestJS ficou em sexto com 1.393 tokens, posição melhor do que o peso aparente dos seus decoradores e módulos sugeriria. A estrutura é repetitiva, mas substitui código de inicialização e roteamento que outras stacks precisam descrever explicitamente. Em termos de tokens absolutos por entidade, ele se sustenta. O peso real do NestJS aparece quando o número de entidades cresce — voltaremos a esse ponto na análise de complexidade.

Tokens de output gerados por stack (1 entidade, mediana de 3 rodadas)
Tokens de output gerados por stack (1 entidade, mediana de 3 rodadas)Django + DRF681Laravel725Elysia + Drizzle1.205Gin + GORM1.247Fiber + Ent1.317NestJS + Prisma1.393Spring Boot + JPA1.514Hono + Drizzle1.587ASP.NET + EF Core1.98105001.0001.5002.000tokens

Top 3 destacado em roxo. Modelo: Qwen 2.5 Coder 14B via Ollama.

Django com DRF: o líder do benchmark

Django dominou o benchmark com 681 tokens, fruto direto do Django REST Framework. O ModelViewSet substitui as cinco rotas REST por uma única classe; o DefaultRouter expõe o conjunto inteiro com duas linhas no urls.py. É o que acontece quando o framework foi desenhado em torno de CRUD e a IA pode aproveitar essas convenções sem reinventar a roda.

  • ModelViewSet automatiza listar, criar, recuperar, atualizar e remover sem código adicional.
  • DefaultRouter expõe todas as rotas REST a partir de um único register.
  • ModelSerializer infere campos a partir do model, eliminando duplicação.
  • Migrations automáticas via makemigrations e migrate.

No benchmark, o Django entregou uma ViewSet de quatro linhas que cobre todo o CRUD:

customers/views.py — Django + DRFpython
from rest_framework import viewsets
from .models import Customer
from .serializers import CustomerSerializer

class CustomerViewSet(viewsets.ModelViewSet):
    queryset = Customer.objects.all()
    serializer_class = CustomerSerializer

O roteador completa o trabalho em mais três linhas, sem precisar declarar cada endpoint individualmente:

customers/urls.py — Django + DRFpython
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import CustomerViewSet

router = DefaultRouter()
router.register(r'customers', CustomerViewSet)

urlpatterns = [path('', include(router.urls))]

Laravel: o benchmark clássico

O Laravel ficou em segundo com 725 tokens, a apenas 44 de distância do Django. Uma única linha cria todas as rotas REST de um recurso, e o ecossistema entrega praticamente tudo que um CRUD precisa: validação, migrações, ORM, autenticação e testes.

  • Convenções maduras que reduzem decisões repetitivas.
  • CLI poderosa para gerar controllers, models e migrations.
  • Eloquent como ORM robusto e expressivo.
  • Grande ecossistema com pacotes consolidados.

No benchmark, o Laravel gerou o roteamento completo em uma única linha de código. Toda a definição dos cinco endpoints REST cabe aqui:

routes/api.php — Laravelphp
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\API\CustomerController;

Route::apiResource('customers', CustomerController::class);

O controller que dá vida a essas rotas é igualmente conciso, delegando validação para Form Requests e serialização para Resources:

app/Http/Controllers/API/CustomerController.php — Laravelphp
class CustomerController extends Controller
{
    public function index(Request $request)
    {
        $page = $request->input('page', 1);
        $pageSize = $request->input('pageSize', 10);
        return CustomerResource::collection(
            Customer::paginate($pageSize, ['*'], 'page', $page)
        );
    }

    public function store(CustomerRequest $request)
    {
        $customer = Customer::create($request->validated());
        return new CustomerResource($customer);
    }

    public function show(Customer $customer)
    {
        return new CustomerResource($customer);
    }

    public function update(CustomerRequest $request, Customer $customer)
    {
        $customer->update($request->validated());
        return new CustomerResource($customer);
    }

    public function destroy(Customer $customer)
    {
        $customer->delete();
        return response()->json(null, 204);
    }
}

Bun com Elysia e Drizzle: tipagem moderna em TypeScript

O Bun combina um runtime moderno com suporte nativo a TypeScript. O Elysia oferece uma API extremamente concisa para definir rotas e validações, e o Drizzle mantém o acesso ao banco tipado, leve e próximo ao SQL.

  • Código enxuto, com poucas linhas para expor uma rota completa.
  • Excelente inferência de tipos de ponta a ponta.
  • Alta performance no runtime do Bun.
  • Baixo overhead de boilerplate e decoradores.

No benchmark, todas as cinco rotas do CRUD são encadeadas em um único objeto Elysia, sem decoradores e sem arquivos extras de roteamento:

src/routes/customers.ts — Elysiatypescript
import { Elysia } from 'elysia';
import { CustomerService } from '../services/CustomerService';

const customerService = new CustomerService();

export const customersRoute = new Elysia()
  .post('/', async ({ body }) => {
    return await customerService.create(body);
  })
  .get('/', async ({ query }) => {
    const page = parseInt(query.page ?? '1', 10);
    const pageSize = parseInt(query.pageSize ?? '10', 10);
    return await customerService.list(page, pageSize);
  })
  .get('/:id', async ({ params }) => {
    return await customerService.get(params.id);
  })
  .put('/:id', async ({ params, body }) => {
    return await customerService.update(params.id, body);
  })
  .delete('/:id', async ({ params }) => {
    await customerService.delete(params.id);
    return { success: true };
  });

Go com Ent: produtividade com geração de código

O Go tradicionalmente é mais explícito que linguagens dinâmicas, mas o Ent reduz significativamente o boilerplate. Ao definir o schema da entidade, grande parte das operações de CRUD é gerada automaticamente, com tipos fortes e queries seguras.

Isso aproxima o Go da produtividade de frameworks mais opinativos, sem abrir mão de binários simples e alta performance em produção. Ainda assim, ficou na zona média da tabela com 1.317 tokens: cada handler precisa lidar explicitamente com parsing, validação e tratamento de erro, sem o açúcar sintático de frameworks dedicados a CRUD. Go é uma escolha excelente para serviços de infraestrutura ou APIs de alta performance — para CRUD de aplicação, outras stacks pagam menos tokens por uma estrutura equivalente.

handlers/customer_handler.go — Fiber + Ent (trecho)go
func (h *CustomerHandler) Create(c *fiber.Ctx) error {
    var input struct {
        Name     string `json:"name" validate:"required"`
        Email    string `json:"email" validate:"required,email"`
        Document string `json:"document" validate:"required"`
        Phone    string `json:"phone,omitempty"`
        Status   string `json:"status,omitempty"`
    }

    if err := c.BodyParser(&input); err != nil {
        return c.Status(http.StatusBadRequest).JSON(fiber.Map{
            "error":   "Invalid input",
            "message": err.Error(),
        })
    }

    customer, err := h.client.Customer.Create().
        SetName(input.Name).
        SetEmail(input.Email).
        SetDocument(input.Document).
        SetPhone(input.Phone).
        SetStatus(input.Status).
        Save(context.Background())
    if err != nil {
        return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
            "error":   "Failed to create customer",
            "message": err.Error(),
        })
    }

    return c.JSON(customer)
}

NestJS com Prisma: arquitetura corporativa

O NestJS é excelente para sistemas complexos e times grandes. Decoradores, módulos e injeção de dependência criam uma arquitetura previsível, e seria razoável esperar que esse overhead arquitetural pagasse caro em tokens.

  • Arquitetura consistente entre módulos e features.
  • Separação clara de responsabilidades entre controllers, services e repositories.
  • Forte aderência a padrões corporativos e DDD.
  • Ecossistema maduro de integrações e testes.

Os números mostraram o contrário. NestJS ficou em sexto lugar com 1.393 tokens de mediana, próximo do meio da tabela. Os decoradores são repetitivos, mas substituem código de inicialização e roteamento que outras stacks precisam descrever explicitamente — e a IA aproveita esse padrão para gerar handlers compactos:

src/customer/controllers/customer.controller.ts — NestJS (trecho)typescript
@Controller('customers')
export class CustomerController {
  constructor(private readonly customerService: CustomerService) {}

  @Post()
  async create(@Body() dto: CreateCustomerDto) {
    try {
      const created = await this.customerService.create(dto);
      return { data: created };
    } catch (error) {
      return { error: true, message: error.message };
    }
  }

  @Get()
  async findAll(
    @Query('page') page: number,
    @Query('pageSize') pageSize: number,
  ) {
    return { data: await this.customerService.findAll(page, pageSize) };
  }

  @Get(':id')
  async findOne(@Param('id') id: string) {
    return { data: await this.customerService.findOne(id) };
  }

  @Put(':id')
  async update(
    @Param('id') id: string,
    @Body() dto: UpdateCustomerDto,
  ) {
    return { data: await this.customerService.update(id, dto) };
  }

  @Delete(':id')
  async remove(@Param('id') id: string) {
    return { data: await this.customerService.remove(id) };
  }
}

Spring Boot com JPA: o enterprise da JVM

Spring Boot ficou em sétimo lugar com 1.514 tokens. O JpaRepository elimina parte do trabalho — a interface vazia ganha automaticamente findAll, findById, save e delete — mas o restante do ecossistema cobra seu preço em ergonomia explícita: anotações em cada propriedade da entidade, getters e setters obrigatórios em Java tradicional, configuração separada de DataSource e camada de service formalmente injetada via @Autowired. Em troca, o time recebe um framework com décadas de aderência ao mundo corporativo.

CustomerRepository.java — Spring Boot + JPAjava
package com.example.demo.repository;

import com.example.demo.model.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;

public interface CustomerRepository extends JpaRepository<Customer, UUID> {
}

O controller equivalente mantém o padrão Spring de injeção via @Autowired e roteamento via @RequestMapping. Cada handler descreve explicitamente o método HTTP e o tipo de retorno:

CustomerController.java — Spring Boot (trecho)java
@RestController
@RequestMapping("/customers")
public class CustomerController {

    @Autowired
    private CustomerService customerService;

    @GetMapping
    public Page<Customer> getAllCustomers(Pageable pageable) {
        return customerService.getAllCustomers(pageable);
    }

    @PostMapping
    public ResponseEntity<Customer> createCustomer(@RequestBody Customer customer) {
        Customer created = customerService.createCustomer(customer);
        return ResponseEntity.created(null).body(created);
    }

    @PutMapping("/{id}")
    public ResponseEntity<Customer> updateCustomer(
        @PathVariable UUID id,
        @RequestBody Customer customerDetails
    ) {
        return ResponseEntity.ok(customerService.updateCustomer(id, customerDetails));
    }
}

ASP.NET Core com EF Core: o verboso por design

C# com ASP.NET Core e Entity Framework Core terminou em nono lugar com 1.981 tokens, quase três vezes o consumo do Django. Isso não é necessariamente uma crítica: a plataforma .NET tradicionalmente prioriza expressividade, separação rígida de responsabilidades e validação declarativa via DataAnnotations. Cada uma dessas escolhas adiciona tokens à geração.

  • DTOs separados para criação, atualização e leitura, todos com anotações de validação próprias.
  • Repository pattern formal com interface dedicada e implementação injetada via DI.
  • OnModelCreating no DbContext para configurar comportamentos do EF Core.
  • Program.cs com builder pattern, registro de serviços e configuração de pipeline HTTP.

O resultado é código altamente estruturado, mas a IA precisa descrever cada uma dessas peças explicitamente para entregar o CRUD. A diferença em relação ao Django, que faz quase tudo via ModelViewSet, fica visível no Program.cs:

Program.cs — ASP.NET Core (trecho)csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddDbContext<CustomerDbContext>(options =>
{
    options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"));
});

builder.Services.AddScoped<ICustomerRepository, CustomerRepository>();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

O papel do framework

A linguagem é apenas parte da equação. O maior impacto no consumo de tokens está em decisões do framework escolhido.

  • Convenções que evitam reescrever a mesma estrutura a cada feature.
  • Geração automática de código a partir de schemas ou definições.
  • Nível de abstração escolhido para encapsular regras comuns.
  • Verbosidade exigida pela arquitetura de referência do framework.

Em muitos casos, o framework influencia mais o orçamento de tokens do que a linguagem em si. Trocar de framework dentro da mesma linguagem pode reduzir o consumo pela metade.

Cada arquivo extra que sua arquitetura exige é um imposto invisível sobre cada conversa com a IA.

A complexidade do consumo de tokens

O benchmark mediu um caso pontual: uma entidade Customer com sete campos. Sistemas reais não têm uma entidade, têm dezenas. Vale formalizar como esse consumo de tokens cresce quando N entidades entram no escopo, porque é daí que vem a diferença entre uma decisão técnica trivial e uma decisão estratégica.

Para todas as stacks avaliadas, o consumo de tokens para gerar um sistema com N entidades é aproximadamente linear em N:

T(N) ≈ a + b × N

Onde a é o custo fixo de boilerplate (configuração de banco, bootstrap do servidor, módulo raiz) e b é o custo marginal por entidade (migration, model, controller, validação, rota). Em notação Big O, isso é O(N): o crescimento é proporcional ao número de entidades, sem termos quadráticos ou exponenciais. A boa notícia é que linearidade significa previsibilidade; a má é que o coeficiente angular b cobra dividendos a cada nova entidade.

Estimando a e b a partir da proporção de arquivos fixos versus arquivos por entidade no benchmark, e aplicando aos tokens medidos com N=1, chegamos aos seguintes parâmetros e projeções para um sistema com dez entidades:

  • Django com DRF: a ≈ 40 tokens, b ≈ 640 tokens por entidade. Projeção para N=10: cerca de 6.440 tokens.
  • Laravel: a ≈ 25, b ≈ 700. Projeção para N=10: cerca de 7.025 tokens.
  • Bun com Elysia e Drizzle: a ≈ 170, b ≈ 1.035. Projeção para N=10: cerca de 10.520 tokens.
  • Go com Gin e GORM: a ≈ 140, b ≈ 1.107. Projeção para N=10: cerca de 11.210 tokens.
  • Go com Fiber e Ent: a ≈ 210, b ≈ 1.107. Projeção para N=10: cerca de 11.280 tokens.
  • Spring Boot com JPA: a ≈ 250, b ≈ 1.264. Projeção para N=10: cerca de 12.890 tokens.
  • NestJS com Prisma: a ≈ 75, b ≈ 1.318. Projeção para N=10: cerca de 13.255 tokens.
  • Bun com Hono e Drizzle: a ≈ 230, b ≈ 1.357. Projeção para N=10: cerca de 13.800 tokens.
  • C# com ASP.NET Core e EF Core: a ≈ 400, b ≈ 1.581. Projeção para N=10: cerca de 16.210 tokens.

A leitura é reveladora. Django e Laravel mantêm a liderança em qualquer N porque têm os menores coeficientes b da tabela, fruto direto de convenções fortes (ModelViewSet do DRF, Route::apiResource do Laravel). C# com ASP.NET, por outro lado, tem o maior b — cada nova entidade exige DTO de criação, DTO de atualização, DTO de leitura, validações em cada um e configurações no DbContext. Em projetos grandes, a diferença entre o primeiro e o último ultrapassa duas vezes e meia o consumo de tokens.

NestJS é outro caso interessante: ficou em sexto com N=1, mas seu b é alto. Cada nova entidade exige module, controller, service, dto de criação, dto de atualização, interface, model e schema. Em projetos com poucas entidades, NestJS é competitivo; em sistemas grandes, o coeficiente angular cobra o preço e ele tende a ultrapassar tanto Spring Boot quanto Go.

Esse modelo linear se mantém enquanto entidades vivem isoladas umas das outras. Quando começam a se relacionar entre si, e a IA precisa descrever esses relacionamentos para gerar o código consistente, a análise muda. Se cada uma das N entidades pode ter relação com cada uma das outras, descrever o grafo completo cresce no pior caso com O(N²). Na prática, sistemas reais têm poucos hubs muito conectados e muitas entidades quase isoladas, então o crescimento fica entre O(N) e O(N²), tipicamente mais próximo do linear.

Outro custo entra em cena quando o sistema cresce: a manutenção. Cada nova modificação exige que o modelo carregue parte do código existente como contexto, para não quebrar invariantes que já estão lá. Esse contexto também escala com N. O custo total de propriedade do código gerado por IA tem dois termos: o de geração inicial e o de cada iteração subsequente, ambos crescendo com N. Stacks que minimizam tokens por entidade reduzem os dois ao mesmo tempo, e o ganho composta a cada iteração.

Por fim, existe um teto absoluto. Modelos têm janela de contexto finita. Quando o código existente excede essa janela, a IA precisa selecionar o que ler, e essa seleção tem custo próprio. Stacks que entregam contexto denso, com poucos arquivos e baixa indireção, preservam mais tokens para a regra de negócio nova. Stacks pulverizadas em muitos arquivos forçam o modelo a ler mais para gastar a mesma quantia de raciocínio efetivo.

Em projetos longos, o coeficiente angular b importa mais do que o ponto de partida a.

Em projetos com expectativa de longa vida e dezenas de entidades, escolher uma stack com b baixo paga dividendos compostos a cada iteração. Em projetos pequenos ou provas de conceito, a diferença é marginal e outros fatores como DX, familiaridade do time e ecossistema costumam dominar a decisão.

Aderência do código gerado

Tokens gastos não é a mesma coisa que valor entregue. Um código que gasta poucos tokens mas precisa ser reescrito antes de rodar acaba custando muito mais ao longo do ciclo. Para entender esse outro eixo, revisamos linha a linha o output de cada stack e classificamos quanto retrabalho seria necessário para o código virar funcional.

  • Laravel: aderência alta. Convenções respeitadas, Route::apiResource correto, Eloquent com fillable e casts idiomáticos, FormRequest com regras válidas. Rodaria com ajustes mínimos.
  • Django com DRF: aderência alta. ModelViewSet correto, DefaultRouter idiomático, ModelSerializer com Meta apropriado. Pequena correção pontual no UUIDField default (deveria ser uuid.uuid4) e organização de pastas que mistura models/views/serializers fora do padrão app único.
  • Go com Gin e GORM: aderência alta. Estrutura idiomática, separação clara entre handlers, services e routes. Pequenos ajustes pontuais como imports faltantes para strconv na paginação.
  • Spring Boot com JPA: aderência média-alta. Entity correta com Jakarta Persistence, Repository limpo, Service idiomático. Inconsistência no arquivo de configuração (mistura javax.persistence com jakarta.persistence) que precisaria de ajuste em projetos Spring Boot 3+.
  • C# com ASP.NET Core: aderência alta. Estrutura idiomática completa. Único bug é o atributo [Index(IsUnique = true)] em propriedade, que em EF Core moderno precisa ser configurado via Fluent API. Resto do código roda com ajustes mínimos.
  • Bun com Elysia e Drizzle: aderência média. Estrutura limpa do Elysia, mas o Drizzle ainda saiu com pequenas inconsistências de API em alguns runs (withCount, sintaxe de pgEnum). Precisa revisão pontual no service.
  • Go com Fiber e Ent: aderência média. Conceitos corretos, mas o modelo deixou paths literais em alguns arquivos e tem chain de update mal encadeado. Imports incompletos em handlers de update.
  • Bun com Hono e Drizzle: aderência média. Estrutura correta de roteamento Hono, sem confundir com outras runtimes. Pequenos detalhes de import e schema que pedem revisão.
  • NestJS com Prisma: aderência média. O Prisma foi gerado corretamente desta vez, com schema.prisma e PrismaService. Validação via class-validator presente. Algumas inconsistências em try/catch padronizado que poderiam virar um filtro global.

Vale observar que essa aderência depende fortemente do tamanho do modelo. Modelos pequenos, com menos parâmetros, tendem a alucinar em frameworks de cauda longa — bibliotecas menos populares aparecem pouco no dado de treino e o modelo recorre a sintaxes de bibliotecas vizinhas mais documentadas. Com Qwen 2.5 Coder 14B esse efeito ficou raro; com modelos menores, é comum trocar Hono por Next.js ou Prisma por Mongoose em geração de CRUD.

O custo efetivo de uma stack é tokens de geração mais tokens de retrabalho. Em modelos pequenos, stacks com pouca documentação no treino pagam o segundo termo silenciosamente.

Esse eixo confirma a leitura do benchmark. Django e Laravel não só gastaram menos tokens, como entregaram outputs próximos de funcional. C# foi o mais verboso, mas também entregou código altamente estruturado e quase pronto. As stacks no meio da tabela exigem ajustes pequenos, esperados em qualquer geração de código com IA neste momento.

O que a Anthropic diz sobre isso

Esse raciocínio não é apenas opinião de quem usa IA no dia a dia. Em artigo recente sobre Claude Code em grandes bases de código, a própria Anthropic reforça que o entorno do modelo importa mais do que o modelo isolado.

O ecossistema construído ao redor do modelo, o harness, determina o desempenho do Claude Code mais do que o modelo sozinho.

Traduzindo para o debate de stacks: o framework, suas convenções, o ORM escolhido e a arquitetura padrão funcionam como o harness do modelo. É essa camada que decide se a IA gasta tokens entendendo a estrutura ou avançando na regra de negócio.

A capacidade do Claude de ajudar em uma base de código grande é limitada pela sua capacidade de encontrar o contexto certo.

Stacks enxutas como Bun com Elysia e Drizzle entregam essa legibilidade de forma natural: poucos arquivos, tipos inferidos e baixa indireção. Frameworks mais opinativos compensam com convenções fortes, mas o preço é pago em tokens por arquivo.

Contexto demais carregado em cada sessão degrada a performance; contexto de menos deixa o Claude navegando às cegas.

O ponto é direto: existe um equilíbrio. Stacks que minimizam o ruído estrutural deixam mais espaço para o que importa, sem cair no extremo oposto de exigir contexto adicional a cada prompt.

Qual stack escolher

A escolha depende de qual prioridade está no topo do projeto. Algumas combinações funcionam melhor em determinados cenários.

  • Prioridade em menor consumo de tokens: Django com DRF ou Laravel.
  • Prioridade em produtividade e maturidade: Laravel ou Django.
  • Prioridade em type safety moderno: Bun com Elysia e Drizzle.
  • Prioridade em performance e binários simples: Go com Fiber e Ent.
  • Prioridade em arquitetura corporativa JVM: Spring Boot com JPA.
  • Prioridade em integração com ecossistema Microsoft: ASP.NET Core com Entity Framework.
  • Prioridade em arquitetura previsível em TypeScript: NestJS com Prisma.

Minha recomendação

Para projetos onde o objetivo é entregar valor de negócio rápido com IA escrevendo boa parte do código, Django com DRF e Laravel terminaram tecnicamente empatados no topo do benchmark — diferença de 44 tokens entre eles é ruído. A escolha entre os dois passa por familiaridade do time e ecossistema preferido, não por consumo. Ambos combinam o menor consumo de tokens do benchmark com convenções fortes que reduzem a chance de o modelo se perder no caminho.

Quando a prioridade é tipagem moderna de ponta a ponta, runtime de alta performance e um time que vive em TypeScript, Bun com Elysia e Drizzle segue como uma escolha forte. Ficou em terceiro no consumo de tokens, entrega inferência ponta a ponta que nenhuma outra stack do benchmark oferece, e continua sendo uma aposta sólida para times que querem fugir do peso do Node tradicional.

Para projetos enterprise com restrições de stack (JVM ou .NET por questões corporativas), Spring Boot e ASP.NET Core entregam código estruturado e funcional, com o custo esperado em tokens. Não é razoável esperar que essas plataformas, desenhadas em torno de tipagem forte e separação rígida de responsabilidades, fiquem no topo de um ranking de concisão — mas o ganho de previsibilidade é o que justifica o consumo.

Conclusão

Ao utilizar IA para desenvolvimento, o consumo de tokens passa a ser um fator estratégico, não apenas um detalhe técnico. Stacks mais concisas reduzem custo, aceleram a geração, simplificam iterações e liberam contexto para regras de negócio.

A melhor stack é aquela que maximiza a produtividade do time e permite que a IA concentre esforços na lógica que diferencia o produto.

Django com DRF e Laravel terminaram tecnicamente empatados no topo, ambos combinando convenções maduras, ecossistema robusto e o menor consumo de tokens entre as stacks avaliadas — combinação difícil de superar para projetos que precisam acelerar entrega com apoio de IA. Bun com Elysia e Drizzle vem logo atrás, com a vantagem adicional de tipagem moderna e runtime de alta performance para times que vivem em TypeScript. No outro extremo, ASP.NET Core mostrou que o custo do estilo enterprise pode chegar a quase três vezes o de um framework de CRUD especializado — uma decisão a ser feita conscientemente.

Limitações desta análise

Os números acima foram produzidos por um modelo específico em condições específicas, e é importante deixar isso transparente para o leitor poder interpretar os resultados com a calibração correta.

  • Modelo utilizado: Qwen 2.5 Coder 14B, rodando localmente via Ollama no MacBook Pro M3 com 18GB de memória. Modelos comerciais maiores como Claude Opus, GPT-5 ou Gemini Ultra podem gerar código mais elaborado e, portanto, mais tokens em termos absolutos. O ranking entre stacks tende a se preservar, mas os valores absolutos não são intercambiáveis.
  • Cada família de modelo possui um tokenizer próprio. As contagens reportadas aqui refletem o tokenizer do Qwen e não são diretamente comparáveis com Claude, GPT ou Gemini para a mesma porção de código. O que se mantém consistente entre tokenizers é o ranking relativo.
  • Cada stack foi gerada três vezes para calcular a mediana. Os runs de uma mesma stack ficaram em uma faixa estreita de variação, o que torna a mediana estável e o ranking confiável.
  • Os coeficientes a e b da seção de complexidade são estimativas derivadas da proporção entre arquivos fixos e arquivos por entidade no benchmark de uma entidade. Validar essas projeções exigiria rodar o benchmark com N variados (próximo ciclo).
  • Custo total de propriedade depende também de talento disponível, suporte do ecossistema e custos operacionais fora da geração de código.

Próximos passos

O próximo ciclo deste benchmark vai responder perguntas que ainda ficaram em aberto.

  • Rodar o mesmo prompt em Claude, GPT e Gemini para comparar o ranking entre tokenizers comerciais e medir se as posições se mantêm com modelos de fronteira.
  • Validar empiricamente a fórmula T(N) ≈ a + b × N rodando o benchmark com N variando entre uma e dez entidades, medindo a e b de verdade em vez de estimar.
  • Medir o tempo até primeiro deploy local, executando o código gerado em cada stack, instalando dependências e subindo o servidor com banco.
  • Medir o custo de manutenção: quantos tokens cada stack gasta ao adicionar um campo novo na entidade, não só ao criar do zero.
  • Ampliar para stacks ainda não cobertas: Rails, FastAPI, Phoenix, Quarkus.
  • Avaliar como cada stack se comporta em iterações de longo prazo, simulando seis meses de evolução de produto.

Se você também está utilizando IA para acelerar o desenvolvimento, essa comparação pode ajudar a escolher uma stack mais eficiente e alinhada à estratégia tecnológica do seu produto.

Glossário

Termos técnicos usados ao longo do artigo, em ordem de aparição:

  • CRUD — sigla para Create, Read, Update, Delete. As quatro operações básicas que toda aplicação faz sobre dados persistidos.
  • REST — estilo arquitetural de API que usa verbos HTTP (GET, POST, PUT, DELETE) e URLs como recursos.
  • Endpoint — URL específica de uma API que aceita requisições e retorna respostas.
  • Boilerplate — código repetitivo que toda feature precisa ter, mas não carrega regra de negócio (configuração, imports, scaffolding).
  • ORM — Object-Relational Mapping. Biblioteca que traduz operações em objetos da linguagem para queries SQL no banco.
  • Migration — script versionado que altera o schema do banco de dados de forma controlada e reversível.
  • DTO — Data Transfer Object. Objeto desenhado especificamente para carregar dados entre camadas ou pela rede, sem comportamento.
  • Decoradores — sintaxe que aplica metadados ou comportamento a uma classe, método ou propriedade. Existe em TypeScript, Python, Java, C# e outros.
  • Tokenizer — algoritmo que quebra texto em unidades menores (tokens) antes de o modelo processar. Cada família de modelo tem o seu, e contagens não são intercambiáveis.
  • Harness — conjunto de ferramentas, prompts de sistema e infraestrutura que envolve um modelo de IA para torná-lo útil em uma aplicação real. Inclui leitura de arquivos, gerenciamento de contexto e ferramentas disponíveis ao modelo. No debate de stacks deste artigo, os frameworks e suas convenções funcionam como o harness da IA na hora de gerar código.
  • Janela de contexto — quantidade máxima de tokens que um modelo aceita em uma única chamada, somando o prompt enviado e a resposta gerada.
  • Big O — notação que descreve como o tempo ou o consumo de uma operação cresce conforme a entrada aumenta. O(N) é crescimento linear, O(N²) é quadrático.
  • Coeficiente angular — em uma reta da forma y = a + b × x, o b é o coeficiente angular: quanto y cresce a cada unidade que x aumenta.