Entrevistas para engenheiro de software seguem um padrão conhecido na maioria das empresas: uma ligação com o recrutador, uma ou duas rodadas de programação, uma rodada de design de sistemas para cargos plenos e sêniores, e uma rodada comportamental. Abaixo estão 20 perguntas de entrevista para engenheiro de software com respostas, agrupadas por rodada. Cada código de exemplo foi testado contra uma versão de força bruta com milhares de entradas aleatórias antes da publicação.
O que cada rodada avalia
| Rodada | Formato típico | O que estão avaliando |
|---|---|---|
| Triagem com recrutador | 20 a 30 minutos por telefone | Experiência, motivação, faixa salarial, logística |
| Programação | 45 a 60 minutos em um editor compartilhado | Resolução de problemas, código limpo, testes, falar enquanto trabalha |
| Fundamentos de CS | Misturada à programação ou em uma rodada separada | Se você entende os sistemas que usa |
| Design de sistemas | 45 a 60 minutos em um quadro branco ou documento | Trade-offs, escala e comunicação clara |
| Comportamental | 30 a 45 minutos | Responsabilidade pelo trabalho, colaboração, como lida com falhas |
Entrevistas para recém-formados são sobretudo de programação e comportamentais. Entrevistas para cargos sêniores acrescentam mais design de sistemas e esperam respostas mais profundas sobre projetos passados. Para a ligação com o recrutador, veja nosso guia de perguntas de entrevista por telefone.
Perguntas de programação
Em toda rodada de programação, diga sua abordagem antes de digitar, informe a complexidade de tempo e espaço e teste com um exemplo pequeno no final. Nossa folha de consulta de Big O cobre a parte de complexidade.
1. Soma de dois números: retorne os índices de dois valores cuja soma seja o alvo
Guarde o índice de cada número em um mapa de hash. Para cada número novo, verifique se o seu complemento já está no mapa.
def two_sum(nums, target):
seen = {}
for i, x in enumerate(nums):
if target - x in seen:
return [seen[target - x], i]
seen[x] = i
return []
print(two_sum([2, 7, 11, 15], 9)) # [0, 1]
Tempo e espaço O(n). A versão de força bruta verifica cada par em O(n²), e os entrevistadores normalmente querem que você a mencione primeiro e depois a melhore.
2. Parênteses válidos: verifique se todo colchete é fechado na ordem certa
Empilhe os colchetes de abertura em uma pilha. Ao encontrar um colchete de fechamento, o topo da pilha precisa ser o seu par de abertura.
def is_valid(s):
pairs = {")": "(", "]": "[", "}": "{"}
stack = []
for ch in s:
if ch in pairs:
if not stack or stack.pop() != pairs[ch]:
return False
else:
stack.append(ch)
return not stack
print(is_valid("([]{})"), is_valid("(]")) # True False
Tempo e espaço O(n). A última linha é importante: uma string como "((" deixa itens na pilha e não é válida.
3. Mesclar intervalos sobrepostos
Ordene pelo início e depois estenda o último intervalo mesclado ou comece um novo.
def merge_intervals(intervals):
merged = []
for start, end in sorted(intervals):
if merged and start <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], end)
else:
merged.append([start, end])
return merged
print(merge_intervals([[1, 3], [2, 6], [8, 10], [15, 18]]))
# [[1, 6], [8, 10], [15, 18]]
Tempo O(n log n) por causa da ordenação. Pergunte se intervalos que apenas se tocam, como [1, 2] e [2, 3], devem ser mesclados. Esta versão os mescla.
4. Inverter uma lista encadeada
Percorra a lista uma vez e aponte cada nó para o anterior.
class ListNode:
def __init__(self, val, next=None):
self.val = val
self.next = next
def reverse_list(head):
prev = None
while head:
head.next, prev, head = prev, head, head.next
return prev
Tempo O(n) e espaço extra O(1). Se a atribuição múltipla parecer esperta demais para o entrevistador, escreva em três linhas com uma variável temporária. Código claro vale mais que código curto.
5. Retorne os k elementos mais frequentes
Conte com um mapa de hash e depois pegue os k maiores contadores com um heap.
from collections import Counter
import heapq
def top_k_frequent(nums, k):
counts = Counter(nums)
return [x for x, _ in heapq.nlargest(k, counts.items(), key=lambda p: p[1])]
print(top_k_frequent([1, 1, 1, 2, 2, 3], 2)) # [1, 2]
Tempo O(n log k). Uma pergunta de acompanhamento comum é usar bucket sort por frequência, que chega a O(n).
6. Projete um cache LRU com get e put em O(1)
Você precisa de busca rápida e de reordenação rápida por uso recente. Um mapa de hash junto com uma lista duplamente encadeada faz as duas coisas. Em Python, OrderedDict é exatamente essa combinação.
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.data = OrderedDict()
def get(self, key):
if key not in self.data:
return -1
self.data.move_to_end(key)
return self.data[key]
def put(self, key, value):
if key in self.data:
self.data.move_to_end(key)
self.data[key] = value
if len(self.data) > self.capacity:
self.data.popitem(last=False)
Alguns entrevistadores vão pedir que você construa a lista encadeada por conta própria. Explique a estrutura primeiro: o mapa aponta para os nós da lista, o nó mais recente fica em uma das pontas e a remoção acontece pela outra.
7. Menor caminho em uma grade com paredes
A busca em largura encontra o menor caminho quando cada passo tem o mesmo custo.
from collections import deque
def shortest_path(grid):
rows, cols = len(grid), len(grid[0])
if grid[0][0] or grid[rows - 1][cols - 1]:
return -1
queue = deque([(0, 0, 0)])
seen = {(0, 0)}
while queue:
r, c, steps = queue.popleft()
if (r, c) == (rows - 1, cols - 1):
return steps
for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols and not grid[nr][nc] and (nr, nc) not in seen:
seen.add((nr, nc))
queue.append((nr, nc, steps + 1))
return -1
print(shortest_path([[0, 0, 0], [1, 1, 0], [0, 0, 0]])) # 4
Tempo e espaço O(linhas × colunas). Marque as células como visitadas ao colocá-las na fila, e não ao removê-las, senão a mesma célula entra na fila muitas vezes.
8. Encontre a primeira versão ruim
As versões de 1 a n são boas até certo ponto e ruins depois dele. A busca binária encontra a primeira versão ruim em O(log n) verificações.
def first_bad_version(n, is_bad):
lo, hi = 1, n
while lo < hi:
mid = (lo + hi) // 2
if is_bad(mid):
hi = mid
else:
lo = mid + 1
return lo
O padrão serve para qualquer pergunta de sim ou não que muda de valor uma única vez, algo que aparece com frequência. Para uma lista de problemas que cobrem esses padrões, veja o Blind 75.
Perguntas de fundamentos de CS
9. Qual é a diferença entre processo e thread?
Um processo é um programa em execução com seu próprio espaço de memória. As threads rodam dentro de um processo e compartilham a memória dele. Threads são mais baratas para criar e trocar entre si, e podem compartilhar dados diretamente, mas esse compartilhamento é o motivo de você precisar de locks e de as condições de corrida acontecerem. Processos são isolados, então uma falha em um não derruba os outros.
10. Qual é a diferença entre TCP e UDP?
O TCP estabelece uma conexão e garante entrega ordenada e confiável, com retransmissão e controle de fluxo. O UDP envia pacotes independentes, sem garantia de entrega ou de ordem, e com menos overhead. Use TCP para páginas web, APIs e transferência de arquivos. O UDP serve para voz e vídeo ao vivo e jogos, em que um pacote atrasado já não serve para nada. Nossas perguntas de entrevista sobre redes de computadores vão além.
11. O que acontece quando você digita uma URL no navegador e pressiona Enter?
O navegador resolve o domínio para um endereço IP por meio do DNS, abre uma conexão TCP e faz o handshake TLS no caso de HTTPS. Ele envia uma requisição HTTP, e o servidor, muitas vezes atrás de um balanceador de carga, devolve uma resposta. O navegador analisa o HTML, pede o CSS, o JavaScript e as imagens que ele referencia, e renderiza a página. Aprofunde a parte que o entrevistador escolher.
12. Como funciona uma tabela hash e qual é sua complexidade de tempo?
Uma função hash mapeia cada chave para um balde de um array. Buscas, inserções e remoções são O(1) em média. Quando duas chaves caem no mesmo balde, a tabela resolve a colisão por encadeamento ou sondando outra posição, e se redimensiona quando fica cheia demais. O pior caso é O(n), se muitas chaves colidirem.
13. Quando você escolheria um banco SQL em vez de um NoSQL?
Escolha SQL quando os dados são relacionais, você precisa de joins e transações, e o esquema é relativamente estável: pedidos, pagamentos, contas de usuário. Escolha um armazenamento de documentos ou chave-valor quando os padrões de acesso são simples, o formato dos dados varia ou você precisa escalar escritas em muitas máquinas. Muitos sistemas usam os dois. A resposta que o entrevistador quer é o trade-off, não um vencedor. Nossas perguntas de entrevista sobre APIs REST cobrem a camada de API que costuma ficar por cima.
Perguntas de design de sistemas
14. Projete um encurtador de URLs
Primeiro esclareça os requisitos: quantos links novos por dia, quantas leituras, se os links expiram, se são permitidos apelidos personalizados. Depois cubra a API (criação e redirecionamento), como você gera os códigos curtos (um contador codificado em base 62, ou um código aleatório com verificação de unicidade), o armazenamento (um armazenamento chave-valor do código para a URL) e um cache para os links populares, já que as leituras superam muito as escritas. Termine com analytics e prevenção de abuso, se houver tempo. Percorremos o desenho completo no nosso guia de design de sistemas.
15. Projete um limitador de taxa (rate limiter)
Comece definindo onde ele roda (no API gateway, por usuário ou por chave de API) e o que acontece quando um cliente ultrapassa o limite (HTTP 429 com um tempo para tentar de novo). O token bucket é a escolha comum: cada cliente tem um balde que se reabastece a uma taxa fixa e guarda um número máximo de tokens, e cada requisição consome um. Ele permite pequenas rajadas e, ao mesmo tempo, impõe uma taxa média. Com vários servidores, mantenha os contadores em um armazenamento compartilhado, como o Redis, para que todos os servidores vejam a mesma contagem. Para mais prática, veja nossas perguntas de entrevista sobre design de sistemas.
Perguntas comportamentais
Use uma estrutura clara para cada história: a situação, o que você fez e o resultado, com números quando tiver.
16. Fale sobre você
Dê uma versão de dois minutos da sua carreira que termine com o motivo de querer esta vaga. Mencione um ou dois projetos com resultados concretos. Nosso guia sobre como falar sobre você tem exemplos.
17. Conte sobre um bug difícil que você corrigiu
Resposta de exemplo: "Nosso serviço de checkout começou a dar timeout algumas vezes por dia, sem um padrão aparente. Adicionei IDs de requisição aos logs e descobri que todos os timeouts vinham de uma única consulta ao banco, que só rodava quando um carrinho tinha mais de 50 itens. A consulta não tinha um índice na coluna usada na junção. Criei o índice, a consulta lenta caiu de cerca de 4 segundos para menos de 50 milissegundos, e adicionei um alerta na latência dessa consulta para percebermos mais cedo da próxima vez."
18. Conte sobre uma discordância com um colega de equipe
Resposta de exemplo: "Um colega queria reescrever nosso serviço de notificações em um framework novo, e eu achava que o risco era alto demais antes do lançamento. Em vez de discutir na reunião, sugeri que cada um escrevesse o custo e o risco. Concordamos em corrigir os dois piores problemas do código atual antes do lançamento e planejar a reescrita para o trimestre seguinte. O lançamento saiu no prazo, e fizemos a reescrita dois meses depois, com um plano de testes adequado."
19. Conte sobre um projeto que fracassou
Escolha uma falha real em que você tenha sido responsável por parte do problema. Explique o que deu errado, o que faria de outra forma e o que mudou depois. Os entrevistadores estão verificando honestidade e aprendizado, não um histórico perfeito.
20. Por que você quer trabalhar aqui?
Conecte algo específico do produto ou do trabalho de engenharia da empresa à sua própria experiência. "Eu uso a API de vocês há dois anos e gostaria de trabalhar no lado da plataforma" vale mais que "vocês são uma ótima empresa".
Perguntas para fazer ao entrevistador
- Como é uma semana típica para alguém nesta equipe?
- Como o trabalho é planejado, e quem decide as prioridades?
- Como funcionam a revisão de código e o deploy por aqui?
- Como seria o sucesso nos primeiros seis meses?
- Qual é o problema técnico mais difícil em que a equipe está trabalhando?
Veja perguntas para fazer no fim de uma entrevista para mais.
Como se preparar
- Escolha uma linguagem e use-a em todos os exercícios. Python é a escolha mais comum porque o código é curto. Nossas perguntas de entrevista sobre Python cobrem a própria linguagem.
- Pratique por padrão, não por quantidade: mapas de hash, dois ponteiros, janela deslizante, busca binária, BFS e DFS, heaps e programação dinâmica.
- Resolva problemas em voz alta, com um cronômetro. Uma solução correta, mas silenciosa, pontua menos que uma explicação clara e falada.
- Pratique no editor que você vai usar. Muitas empresas usam o CoderPad, que tem um ambiente de prática (sandbox) gratuito.
- Prepare cinco histórias do seu trabalho que você consiga adaptar à maioria das perguntas comportamentais.
- Para cargos plenos e sêniores, faça pelo menos algumas sessões completas de design de sistemas com alguém que faça perguntas de acompanhamento.
Pratique entrevistas de programação em voz alta
O YesToTheOffer transcreve suas respostas para você revisar como explicou sua solução, com ajuda de programação por capturas de tela em 0.2x. 30 minutos grátis, sem assinatura.



