dezembro 21, 2025
11 min de leitura

Perguntas de entrevista para backend Go: guia prático

interview
career-advice
job-search
Perguntas de entrevista para backend Go: guia prático
Milad Bonakdar

Milad Bonakdar

Autor

Prepare-se para entrevistas backend em Go com perguntas sobre goroutines, channels, context, tratamento de erros, testes, servidores HTTP e design de sistemas.


Introdução

Entrevistas backend em Go costumam avaliar se você consegue construir serviços simples e confiáveis: goroutines e channels, cancelamento com context, tratamento explícito de erros, testes, middleware HTTP e decisões de arquitetura. Uma boa resposta explica o que cada recurso faz e também quando você evitaria usá-lo.

Use este guia para praticar respostas objetivas e conecte cada tema a um projeto real: uma API que você criou, um worker pool que depurou, uma race condition que corrigiu ou um serviço que encerrou com segurança durante um deploy.

Conceitos Essenciais de Go

1. O que torna Go diferente de outras linguagens como Java ou Python?

Resposta: Go foi criado para código de sistemas legível e fácil de manter. Em uma entrevista, vale destacar:

  • Simplicidade: Go evita herança e sobrecarga de métodos; equipes usam composição, interfaces pequenas e limites claros entre pacotes.
  • Concorrência: Goroutines, channels, primitivas de sync e context tornam explícito o trabalho concorrente no backend.
  • Compilação: Go gera binários nativos com inicialização rápida e deploy simples.
  • Biblioteca padrão: Pacotes como net/http, context, encoding/json e testing cobrem muito trabalho backend antes de adicionar frameworks.

Raridade: Comum Dificuldade: Fácil


2. Explique a diferença entre Arrays e Slices.

Resposta:

  • Arrays: Sequências de tamanho fixo de elementos do mesmo tipo. O tamanho faz parte do tipo (por exemplo, [5]int é diferente de [10]int). Eles são tipos de valor; atribuir um array a outro copia todos os elementos.
  • Slices: Visões dinâmicas e flexíveis de um array subjacente. Eles consistem em um ponteiro para o array, um comprimento e uma capacidade. Slices são como referências; passar um slice para uma função permite a modificação dos elementos subjacentes sem copiar todos os dados.

Raridade: Comum Dificuldade: Fácil


3. Como as Interfaces funcionam em Go? O que é implementação implícita?

Resposta: Interfaces em Go são coleções de assinaturas de métodos.

  • Implementação Implícita: Ao contrário de Java ou C#, um tipo não declara explicitamente que implementa uma interface (sem a palavra-chave implements). Se um tipo define todos os métodos declarados em uma interface, ele automaticamente implementa essa interface.
  • Duck Typing: "Se anda como um pato e grasna como um pato, é um pato." Isso desacopla a definição da implementação, tornando o código mais flexível e fácil de simular para testes.

Raridade: Comum Dificuldade: Média


4. O que é a palavra-chave defer e como ela funciona?

Resposta: defer agenda uma chamada de função para ser executada imediatamente antes da função retornar. É comumente usado para limpeza de recursos, como fechar arquivos, desbloquear mutexes ou fechar conexões de banco de dados.

  • Ordem LIFO: As chamadas adiadas são executadas na ordem Last-In-First-Out (último a entrar, primeiro a sair).
  • Avaliação de Argumentos: Os argumentos para funções adiadas são avaliados quando a instrução defer é executada, não quando a chamada é executada.

Exemplo:

func readFile(filename string) {
    f, err := os.Open(filename)
    if err != nil {
        return
    }
    defer f.Close() // Garante que o arquivo seja fechado quando a função sair
    // ... processar arquivo
}

Raridade: Comum Dificuldade: Fácil


Concorrência

5. Explique Goroutines e como elas diferem das threads do sistema operacional.

Resposta:

  • Goroutines: Threads leves gerenciadas pelo runtime Go. Elas começam com uma pequena pilha (por exemplo, 2 KB) que cresce e diminui dinamicamente. Milhares de goroutines podem ser executadas em uma única thread do sistema operacional.
  • Threads do SO: Gerenciadas pelo kernel, têm pilhas grandes fixas (por exemplo, 1 MB) e a troca de contexto é cara.
  • Escalonamento M:N: O runtime Go multiplexa M goroutines em N threads do sistema operacional, lidando com o escalonamento de forma eficiente no espaço do usuário.

Raridade: Muito Comum Dificuldade: Média


6. O que são Channels? Buffered vs. Unbuffered?

Resposta: Channels são condutos tipados que permitem que as goroutines se comuniquem e sincronizem a execução.

  • Unbuffered Channels: Não têm capacidade. Uma operação de envio bloqueia até que um receptor esteja pronto, e vice-versa. Eles fornecem forte sincronização.
  • Buffered Channels: Têm uma capacidade. Uma operação de envio só bloqueia se o buffer estiver cheio. Uma operação de recebimento só bloqueia se o buffer estiver vazio. Eles desacoplam o remetente e o receptor até certo ponto.

Raridade: Comum Dificuldade: Média


7. Como você lida com Race Conditions em Go?

Resposta: Uma race condition ocorre quando várias goroutines acessam memória compartilhada concorrentemente, e pelo menos um acesso é uma escrita.

  • Detecção: Use o detector de corrida integrado: go run -race ou go test -race.
  • Prevenção:
    • Channels: "Não se comunique compartilhando memória; em vez disso, compartilhe memória comunicando."
    • Pacote Sync: Use sync.Mutex ou sync.RWMutex para bloquear seções críticas.
    • Operações Atômicas: Use sync/atomic para contadores ou flags simples.

Raridade: Comum Dificuldade: Difícil


8. Para que serve a instrução select?

Resposta: A instrução select permite que uma goroutine espere por várias operações de comunicação. Ela bloqueia até que um de seus casos possa ser executado, então ela executa esse caso. Se vários estiverem prontos, ela escolhe um aleatoriamente.

  • Timeouts: Podem ser implementados usando time.After.
  • Operações Não Bloqueantes: Um caso default torna o select não bloqueante se nenhum outro caso estiver pronto.

Exemplo:

select {
case msg := <-ch1:
    fmt.Println("Recebido", msg)
case ch2 <- "hello":
    fmt.Println("Enviado hello")
case <-time.After(1 * time.Second):
    fmt.Println("Timeout")
}

Raridade: Média Dificuldade: Média


Tratamento de Erros & Robustez

9. Como funciona o Tratamento de Erros em Go?

Resposta: Go trata erros como valores. As funções retornam um tipo error (geralmente como o último valor de retorno) em vez de lançar exceções.

  • Verificar Erros: Os chamadores devem verificar explicitamente se o erro é nil.
  • Erros Personalizados: Você pode criar tipos de erro personalizados implementando a interface error (que tem um único método Error() string).
  • Wrapping: Go 1.13 introduziu o wrapping de erros (fmt.Errorf("%w", err)) para adicionar contexto, preservando o erro original para inspeção (usando errors.Is e errors.As).

Raridade: Comum Dificuldade: Fácil


10. O que são Panic e Recover? Quando você deve usá-los?

Resposta:

  • Panic: Interrompe o fluxo normal de controle e começa a entrar em pânico. É semelhante a uma exceção, mas deve ser reservado para erros irrecuperáveis (por exemplo, desreferência de ponteiro nulo, índice fora dos limites).
  • Recover: Uma função integrada que recupera o controle de uma goroutine em pânico. Só é útil dentro de uma função defer.
  • Uso: Geralmente desencorajado para o fluxo de controle normal. Use valores error para condições de erro esperadas. Panic/recover é usado principalmente para situações verdadeiramente excepcionais ou dentro de bibliotecas/frameworks para evitar que uma falha derrube todo o servidor.

Raridade: Média Dificuldade: Média


Design de Sistemas & Backend

11. Como você estruturaria uma aplicação web em Go?

Resposta: Embora Go não imponha uma estrutura, um padrão comum é o "Standard Go Project Layout":

  • /cmd: Aplicações principais (pontos de entrada).
  • /pkg: Código de biblioteca que pode ser usado por aplicações externas.
  • /internal: Código de aplicação e biblioteca privado (aplicado pelo compilador Go).
  • /api: Especificações OpenAPI/Swagger, definições de protocolo.
  • /configs: Arquivos de configuração.
  • Arquitetura Limpa: Separar preocupações em camadas (Delivery/Handler, Usecase/Service, Repository/Data) para tornar o aplicativo testável e sustentável.

Raridade: Comum Dificuldade: Média


12. Como o pacote context funciona e por que ele é importante?

Resposta: O pacote context carrega deadlines, sinais de cancelamento e valores da requisição através de APIs e goroutines.

  • Cancelamento: Se o cliente desconecta ou uma operação pai é cancelada, o trabalho descendente deve observar ctx.Done() e parar.
  • Timeouts: Use context.WithTimeout ou context.WithDeadline em consultas ao banco e chamadas externas, e chame a função cancel retornada, geralmente com defer cancel().
  • Valores: Guarde apenas metadados da requisição, não parâmetros opcionais nem objetos grandes.

Raridade: Muito Comum Dificuldade: Difícil


13. O que é Injeção de Dependência e como ela é feita em Go?

Resposta: Injeção de Dependência (DI) é um padrão de design onde um objeto recebe outros objetos dos quais depende.

  • Em Go: Geralmente implementado passando dependências (como uma conexão de banco de dados ou um logger) para o construtor de um struct ou função de fábrica, geralmente por meio de interfaces.
  • Benefícios: Torna o código mais modular e testável (fácil de trocar um DB real por um mock).
  • Frameworks: Embora a DI manual seja preferida pela simplicidade, bibliotecas como google/wire ou uber-go/dig existem para grafos complexos.

Raridade: Média Dificuldade: Média


Banco de Dados & Ferramentas

14. Como você lida com JSON em Go?

Resposta: Go usa o pacote encoding/json.

  • Struct Tags: Use tags como `json:"nome_do_campo"` para mapear campos de struct para chaves JSON.
  • Marshal: Converte um struct Go para uma string JSON (slice de bytes).
  • Unmarshal: Analisa dados JSON em um struct Go.
  • Streaming: json.Decoder e json.Encoder são melhores para payloads grandes, pois processam fluxos de dados.

Raridade: Comum Dificuldade: Fácil

15. Quais são algumas ferramentas comuns de Go que você usa?

Resposta:

  • go mod: Gerenciamento de dependências.
  • go fmt: Formata o código para o estilo padrão.
  • go vet: Examina o código em busca de construções suspeitas.
  • go test: Executa testes e benchmarks.
  • pprof: Ferramenta de criação de perfil para analisar o uso de CPU e memória.
  • delve: Depurador para Go.

Raridade: Comum Dificuldade: Fácil


Tópicos Avançados & Melhores Práticas

16. O que são Generics em Go e quando você deve usá-los?

Resposta: Generics (introduzidos no Go 1.18) permitem que você escreva funções e estruturas de dados que funcionam com qualquer um de um conjunto de tipos, em vez de um tipo específico.

  • Parâmetros de Tipo: Definidos usando colchetes []. ex., func Map[K comparable, V any](m map[K]V) ...
  • Constraints: Interfaces que definem o conjunto de tipos permitidos (por exemplo, any, comparable).
  • Uso: Use generics quando o mesmo algoritmo ou estrutura de dados for realmente independente do tipo, como sets, helpers ou utilitários de coleções. Comece com código comum; adicione parâmetros de tipo quando a duplicação for real. Se uma interface pequena expressar melhor o comportamento, prefira a interface.

Raridade: Comum Dificuldade: Médio


17. Explique Testes Table-Driven em Go.

Resposta: O teste table-driven é um padrão preferido em Go, onde os casos de teste são definidos como um slice de structs (a "tabela"). Cada struct contém os argumentos de entrada e a saída esperada.

  • Benefícios:
    • Separação clara da lógica de teste e dos dados de teste.
    • Fácil de adicionar novos casos de teste (basta adicionar uma linha à tabela).
    • Mensagens de falha claras mostrando exatamente qual entrada falhou.
  • Exemplo:
func TestAdd(t *testing.T) {
    tests := []struct {
        a, b, expected int
    }{
        {1, 1, 2},
        {2, -2, 0},
    }
    for _, tt := range tests {
        result := Add(tt.a, tt.b)
        if result != tt.expected {
            t.Errorf("Add(%d, %d): esperado %d, obteve %d", tt.a, tt.b, tt.expected, result)
        }
    }
}

Raridade: Comum Dificuldade: Fácil


18. O que é o Middleware Pattern em servidores HTTP Go?

Resposta: Middleware é uma função que envolve um http.Handler para executar lógica de pré ou pós-processamento antes de passar o controle para o próximo handler.

  • Assinatura: func(next http.Handler) http.Handler
  • Casos de Uso: Logging, Autenticação, Recuperação de Panic, Rate Limiting, CORS.
  • Encadeamento: O middleware pode ser encadeado (por exemplo, Log(Auth(Handler))).

Exemplo:

func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Println(r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    })
}

Raridade: Muito Comum Dificuldade: Média


19. Como você implementa o Graceful Shutdown em um servidor Go?

Resposta: O graceful shutdown garante que um servidor pare de aceitar novas requisições, mas termine de processar as requisições ativas antes de sair.

  • Mecanismo:
    1. Escute os sinais do sistema operacional (SIGINT, SIGTERM) usando os/signal.
    2. Crie um context.WithTimeout para permitir uma janela de limpeza (por exemplo, 5-10 segundos).
    3. Chame server.Shutdown(ctx) no http.Server.
    4. Feche as conexões do DB e outros recursos.
  • Importância: Evita perda de dados e erros do cliente durante as implantações.

Raridade: Comum Dificuldade: Difícil


20. Quando você deve usar sync.Map em vez de um mapa regular com um Mutex?

Resposta: sync.Map é uma implementação de mapa thread-safe na biblioteca padrão.

  • Casos de Uso:
    1. Cache Contention: Quando a entrada para uma determinada chave é escrita apenas uma vez, mas lida muitas vezes (por exemplo, caches de carregamento lento).
    2. Chaves Disjuntas: Quando várias goroutines leem, gravam e sobrescrevem entradas para conjuntos de chaves disjuntos.
  • Trade-off: Para casos de uso geral (atualizações frequentes de leitura/gravação), um map regular protegido por um sync.RWMutex é frequentemente mais rápido e tem melhor segurança de tipo (já que sync.Map usa any).

Raridade: Incomum Dificuldade: Difícil

Newsletter subscription

Dicas de carreira semanais que realmente funcionam

Receba as últimas ideias diretamente na sua caixa de entrada

Sua Próxima Entrevista Está a Apenas um Currículo de Distância

Crie um currículo profissional e otimizado em minutos. Não são necessárias habilidades de design—apenas resultados comprovados.

Criar meu currículo

Compartilhar esta publicação

Reduza o Tempo de Escrita do Currículo em 90%

O candidato a emprego médio gasta mais de 3 horas formatando um currículo. Nossa IA faz isso em menos de 15 minutos, levando você à fase de candidatura 12 vezes mais rápido.