Entrevista Rust Backend: Guia de Preparação

Roteiro prático para entrevista Rust backend: ownership em serviços, async Tokio, Axum, SQLx, erros, observabilidade, filas, testes e perguntas para treinar.

Entrevista Rust Backend: Guia de Preparação

Uma entrevista para Rust backend não é igual a uma entrevista genérica de linguagem. O entrevistador quer saber se você entende Rust o suficiente para construir serviços reais: APIs, workers, integrações, persistência, observabilidade e deploy. Saber explicar ownership ajuda, mas não basta. O sinal forte é mostrar que você consegue transformar tipos, erros e concorrência em software operável.

Este guia organiza um roteiro de preparação para quem mira vagas Rust de backend, plataforma, fintech, infraestrutura ou serviços distribuídos. Ele complementa os guias de projetos práticos em Rust, portfólio no GitHub e carreira web backend. Use como checklist antes de entrevistas técnicas, take-homes e conversas com engenharia.

O que a empresa tenta descobrir

A empresa não quer apenas confirmar que você sabe escrever fn main(). Ela tenta responder perguntas de risco:

  • você consegue depurar erro de compilação sem travar?
  • sabe desenhar API HTTP com validação e erros claros?
  • entende async sem bloquear o runtime?
  • modela dados com tipos em vez de strings soltas?
  • escreve testes que pegam regressão real?
  • sabe observar e operar o serviço depois do deploy?
  • consegue conversar sobre trade-offs sem vender Rust como mágica?

Rust é escolhido muitas vezes por segurança, performance e previsibilidade. Em backend, isso aparece em decisões pequenas: evitar estado global mutável, controlar concorrência, tratar falhas externas, versionar contrato de API e expor métricas úteis.

Ownership aplicado a serviços

Perguntas de ownership continuam aparecendo, mas geralmente com contexto prático. Em vez de explicar a regra abstrata, treine respostas com exemplos de backend.

Você pode dizer que usa String ou structs owned na borda da aplicação, porque payloads HTTP e dados vindos do banco normalmente precisam sobreviver à função que os recebeu. Usa &str e referências em funções internas quando quer apenas ler sem tomar posse. Usa Arc para compartilhar estado imutável ou clientes thread-safe entre handlers, como pool de banco, configuração ou cliente HTTP.

Evite parecer dogmático. Clonar uma string pequena para simplificar um handler pode ser aceitável. Clonar um payload grande em loop crítico talvez seja problema. O ponto é demonstrar consciência de custo, não transformar cada clone em pecado.

Perguntas comuns:

  1. Por que um handler Axum recebe State<AppState> em vez de variável global?
  2. Quando você escolheria String em vez de &str em uma struct de request?
  3. O que muda ao colocar um cliente em Arc?
  4. Como você investigaria um borrow checker error em código async?

Uma resposta boa conecta a regra ao ciclo de vida real dos dados.

Async, Tokio e bloqueios acidentais

Backend Rust moderno quase sempre passa por Tokio. O entrevistador pode perguntar o que acontece quando você chama código bloqueante dentro de uma task async. A resposta: você pode prender uma thread do runtime e prejudicar outras tasks. Para I/O assíncrono, use APIs async. Para CPU pesada ou biblioteca bloqueante, considere spawn_blocking, fila dedicada ou processo separado.

Entenda estes pontos:

  • async fn retorna um Future que precisa ser executado por um runtime;
  • .await libera a task para o runtime avançar outras tasks;
  • tokio::spawn exige dados com vida suficiente e normalmente Send + 'static;
  • select!, timeout, channels e semáforos ajudam a controlar concorrência;
  • concorrência sem limite vira incidente.

Se a vaga envolve workers, leia também Rust para background jobs e mensageria com Kafka, RabbitMQ e NATS. É comum a conversa sair de HTTP e ir para filas, retries, idempotência e shutdown gracioso.

Axum, Actix e desenho de API

Você não precisa conhecer todos os frameworks. Precisa mostrar que entende as peças. Em Axum ou Actix, pratique:

  • rotas e extração de path, query, header e JSON;
  • validação de entrada;
  • estado compartilhado com pool de banco;
  • tipos de resposta;
  • middleware para tracing, CORS, timeout ou autenticação;
  • health checks separados de readiness real.

Uma pergunta recorrente é como você estruturaria uma API simples. Uma resposta madura separa handler fino, camada de serviço e repositório quando isso ajuda, mas evita arquitetura teatral para CRUD pequeno. Mostre que você sabe começar simples e extrair camadas quando o domínio pede.

Exemplo de resposta curta: “Eu deixaria o handler responsável por extrair e validar a entrada, chamaria uma função de serviço com tipos já validados, usaria SQLx no repositório, retornaria um erro de domínio que implementa conversão para status HTTP e incluiria tracing com request id”.

Banco de dados: SQLx, transações e migrações

Para backend, banco pesa mais que micro-otimização. Se usar SQLx, saiba explicar por que queries explícitas e checagem em build são úteis. Se usar Diesel, fale sobre schema tipado e ergonomia. O framework importa menos que a maturidade operacional.

Treine estas situações:

  • como abrir e compartilhar pool de conexões;
  • como aplicar migrations em CI ou deploy;
  • quando usar transação;
  • como lidar com constraint unique;
  • como paginar sem matar o banco;
  • como evitar N+1;
  • como testar queries.

Uma boa resposta sobre transação: “Eu usaria transação quando duas mudanças precisam ser atômicas do ponto de vista de negócio, por exemplo criar pedido e reservar estoque. Também pensaria na fronteira com efeitos externos: não chamo API de pagamento dentro de transação longa sem um padrão de idempotência ou outbox”.

Tratamento de erros que ajuda a operação

Rust força você a olhar para erro. Em entrevista, isso é vantagem se você souber explicar. anyhow é ótimo para binários e ferramentas internas, onde contexto humano importa. thiserror é melhor para erros de domínio e bibliotecas, onde o chamador precisa distinguir variantes.

Em APIs, não exponha erro interno cru. Transforme em resposta adequada: 400 para entrada inválida, 404 para recurso ausente, 409 para conflito, 422 para regra de negócio, 500 para falha inesperada. Logue detalhes internos com tracing, mas retorne uma mensagem segura ao cliente.

Perguntas para treinar:

  • Quando usar anyhow e quando usar thiserror?
  • Como mapear erro de banco para status HTTP?
  • Por que evitar unwrap() em caminho de produção?
  • Como adicionar contexto sem vazar segredo no log?

A frase-chave é: erro bom ajuda a pessoa usuária, a pessoa operadora e o código chamador.

Observabilidade desde o primeiro serviço

Muitos candidatos param em “compila”. Um backend real precisa ser observado. Conheça o básico de Tracing e OpenTelemetry em produção: logs estruturados, spans por request, correlação com banco ou fila, métricas e health checks.

Métricas simples já mostram maturidade:

  • requisições por rota e status;
  • latência por percentil;
  • erros por classe;
  • conexões no pool;
  • backlog de fila;
  • tempo de processamento de jobs;
  • falhas de dependências externas.

Se perguntarem como você investigaria lentidão, não responda só “usaria profiler”. Comece por sintomas: rota, status, latência, mudança recente, dependência externa, banco, fila, saturação de CPU, memória e conexões. Depois escolha ferramenta: logs, traces, métricas, query plan, profiler ou flamegraph.

Testes: unidade, integração e contrato

Rust facilita testes, mas entrevista backend quer cobertura útil. Treine três camadas:

  1. Unidade: funções puras de regra de negócio.
  2. Integração: handlers ou serviços com banco de teste.
  3. Contrato: exemplos de request/response, snapshot ou OpenAPI quando fizer sentido.

Para take-home, entregue cargo test, cargo fmt e cargo clippy -- -D warnings passando. Se houver banco, inclua Docker Compose e comandos claros. Se não tiver tempo para teste completo, escreva pelo menos testes para parsing, validação e erros importantes.

Um teste bom não é só caminho feliz. Inclua payload inválido, recurso ausente, conflito, timeout simulado e caso de concorrência quando relevante.

Take-home: como se destacar

Projetos take-home em Rust costumam revelar organização. Antes de escrever muito código, leia o enunciado e defina o mínimo forte. Depois entregue acabamento:

  • README com decisão técnica e comandos;
  • .env.example sem segredos;
  • migrations e dados de exemplo;
  • erros de API previsíveis;
  • logs estruturados;
  • testes significativos;
  • Docker Compose quando o projeto depender de banco;
  • notas honestas sobre limitações.

Não tente impressionar adicionando Kafka, Kubernetes e cache se o problema não pede. Um serviço simples, confiável e fácil de rodar vence uma arquitetura inflada.

Perguntas que você deve fazer

Entrevista também é filtro para você. Boas perguntas mostram senioridade:

  • Onde Rust já roda em produção na empresa?
  • Quais partes ainda estão em outra linguagem?
  • O time usa Axum, Actix, tonic, SQLx, Diesel ou stack própria?
  • Como fazem observabilidade e deploy?
  • Existem padrões para erro, tracing e migrações?
  • Qual foi o último incidente envolvendo serviço Rust?
  • A vaga é para produto, plataforma, infraestrutura, segurança ou dados?

Essas perguntas ajudam a calibrar sua resposta. Uma fintech pode valorizar idempotência e auditoria. Uma empresa de infraestrutura pode valorizar performance, profiling e sistemas distribuídos. Uma startup pode valorizar entrega simples e manutenção.

Roteiro de estudo de 7 dias

Se a entrevista está próxima, use um plano enxuto:

DiaFocoEntrega
1Ownership em backendRevisar String, &str, Arc, Result e erros comuns
2Axum ou ActixCriar duas rotas, validação JSON e erro HTTP
3SQLx ou DieselPool, migration, query, transação e teste simples
4Tokiospawn, timeout, channel, semáforo e bloqueio acidental
5Observabilidadetracing, request id, logs e métricas mínimas
6Testescargo test, integração com banco e casos negativos
7SimuladoExplicar um projeto seu em 10 minutos e responder trade-offs

Se você só puder construir uma coisa, faça uma API Axum com PostgreSQL, um endpoint de criação, um endpoint de listagem, validação, erro de conflito, teste e README. Depois adicione um worker pequeno se a vaga mencionar filas.

Respostas curtas para perguntas comuns

“Por que Rust para backend?”
Rust combina performance previsível, segurança de memória, binários simples e um modelo de erro explícito. Eu usaria quando confiabilidade, concorrência, custo de infraestrutura ou integração com sistemas importam mais que velocidade inicial de protótipo.

“Rust é sempre melhor que Go ou Java?”
Não. Go e Java têm ecossistemas enormes e onboarding mais simples em muitos times. Rust vale mais quando o domínio se beneficia de controle, segurança e tipos fortes, e quando o time aceita a curva de aprendizado.

“Como você lidaria com timeout em chamada externa?”
Definiria timeout explícito, classificaria erro como transitório quando apropriado, usaria retry com backoff e limite, incluiria idempotência para efeitos externos e exporia métrica de falha por dependência.

“O que você evita em produção?”
unwrap() em caminho crítico, runtime async bloqueado por operação síncrona pesada, logs com segredo, erro genérico sem contexto, concorrência sem limite, migrations manuais não versionadas e deploy sem health/readiness.

Checklist final

Antes da entrevista, garanta que você consegue:

  • explicar ownership com exemplos de handler, state e payload;
  • escrever um handler Axum simples de memória ou quase isso;
  • mapear erro de domínio para HTTP;
  • falar de pool, transação e migration;
  • explicar .await, task e bloqueio acidental;
  • citar métricas e logs que colocaria em produção;
  • mostrar um projeto rodando com README e testes;
  • discutir trade-offs sem fanatismo.

Rust recompensa precisão. Em entrevista, a melhor postura é objetiva: explique o que você sabe, reconheça limites e mostre como investigaria. Isso transmite muito mais confiança do que tentar decorar todas as crates do ecossistema.