InterviewHack.ai
Empezar gratis
Blog/Preguntas de entrevista de Go/Golang con código y respuestas (35+)

Preguntas de entrevista de Go/Golang con código y respuestas (35+)

16 de septiembre de 2026

golangbackend

Artículo SEO completo: "Preguntas de entrevista de Go/Golang con código y respuestas (35+)" — 40 preguntas numeradas con respuestas detalladas y ejemplos de código real, optimizado para desarrolladores LATAM que buscan trabajo remoto en dólares.

Preguntas de entrevista de Go/Golang con código y respuestas (40+)

Go es uno de los lenguajes que más tracción tiene en el mercado remoto. Lo usan Google, Uber, Cloudflare, Twitch, Docker — y el pool de candidatos que realmente lo domina sigue siendo pequeño. Esa es tu ventaja.

Esta guía cubre las 40+ preguntas que más caen en entrevistas técnicas de Go, desde conceptos básicos hasta concurrencia avanzada. Cada respuesta tiene código que podés copiar, entender y adaptar.


Preguntas básicas de Go

1. ¿Qué es Go y cuáles son sus características principales?

Go (también llamado Golang) es un lenguaje compilado, tipado estáticamente, creado por Google en 2009. Sus características principales:

  • Compilación rápida — un proyecto grande compila en segundos
  • Garbage collection — manejo automático de memoria
  • Concurrencia nativa — goroutines y channels integrados al lenguaje
  • Tipado estático con inferencia de tipos
  • Sin herencia — composición en vez de herencia
  • Binario único — se compila en un solo ejecutable sin dependencias
go
package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

2. ¿Cuál es la diferencia entre `var` y `:=`?

var es la declaración explícita de variable, funciona dentro y fuera de funciones. := es la declaración corta con inferencia de tipo, solo funciona dentro de funciones.

go
package main

import "fmt"

var globalVar = "soy global" // var funciona a nivel paquete

func main() {
    var x int = 10        // declaración explícita
    var y = 20            // Go infiere el tipo (int)
    z := 30               // declaración corta, equivalente a var z int = 30

    fmt.Println(x, y, z)
    fmt.Println(globalVar)
}

En la entrevista: explicá que := solo vive dentro de funciones y que var se usa cuando necesitás declarar sin inicializar o cuando querés ser explícito sobre el tipo.


3. ¿Cómo funcionan los tipos de datos en Go?

Go tiene tipos básicos, tipos compuestos y tipos de referencia.

go
package main

import "fmt"

func main() {
    // Tipos básicos
    var i int = 42
    var f float64 = 3.14
    var b bool = true
    var s string = "golang"

    // Tipos compuestos
    arr := [3]int{1, 2, 3}           // array — tamaño fijo
    slice := []int{4, 5, 6}          // slice — tamaño dinámico
    m := map[string]int{"a": 1}      // map

    // Tipo de referencia: pointer
    ptr := &i
    fmt.Println(*ptr) // 42

    fmt.Println(i, f, b, s, arr, slice, m)
}

4. ¿Qué es un slice y cómo difiere de un array?

Un array tiene tamaño fijo definido en tiempo de compilación. Un slice es una vista dinámica sobre un array subyacente.

go
package main

import "fmt"

func main() {
    // Array — tamaño fijo, parte del tipo
    arr := [5]int{1, 2, 3, 4, 5}
    fmt.Println(len(arr)) // 5

    // Slice — dinámico, tres componentes: puntero, longitud, capacidad
    s := []int{1, 2, 3}
    s = append(s, 4, 5)
    fmt.Println(len(s), cap(s)) // 5 6 (cap puede crecer)

    // Slice de un array
    sub := arr[1:3]
    fmt.Println(sub) // [2 3]

    // make — slice con longitud y capacidad explícitas
    s2 := make([]int, 3, 10)
    fmt.Println(len(s2), cap(s2)) // 3 10
}

Dato importante: cuando dos slices comparten el mismo array subyacente, modificar uno modifica al otro.

go
a := []int{1, 2, 3, 4, 5}
b := a[1:3]   // b apunta al mismo array
b[0] = 99
fmt.Println(a) // [1 99 3 4 5] — a cambió también

5. ¿Cómo funcionan los maps en Go?

go
package main

import "fmt"

func main() {
    m := map[string]int{
        "alice": 30,
        "bob":   25,
    }

    age := m["alice"]
    fmt.Println(age) // 30

    val, ok := m["charlie"]
    if !ok {
        fmt.Println("charlie no existe, val es:", val) // val es 0 (zero value)
    }

    m["charlie"] = 28
    delete(m, "bob")

    for key, value := range m {
        fmt.Printf("%s: %d\n", key, value)
    }
}

En la entrevista: los maps no son seguros para uso concurrente. Para eso existe sync.Map.


6. ¿Qué es el zero value en Go?

Go inicializa todas las variables a su "zero value" automáticamente.

go
package main

import "fmt"

func main() {
    var i int       // 0
    var f float64   // 0.0
    var b bool      // false
    var s string    // ""
    var p *int      // nil
    var sl []int    // nil (pero len(sl) == 0)
    var m map[string]int // nil

    fmt.Println(i, f, b, s, p, sl, m)
}

7. ¿Cómo funciona el manejo de errores en Go?

Go no tiene excepciones. Los errores son valores que se retornan como último valor de retorno.

go
package main

import (
    "errors"
    "fmt"
)

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("no se puede dividir por cero")
    }
    return a / b, nil
}

type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validación fallida en %s: %s", e.Field, e.Message)
}

func validateAge(age int) error {
    if age < 0 {
        return &ValidationError{Field: "age", Message: "no puede ser negativo"}
    }
    return nil
}

func main() {
    result, err := divide(10, 2)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println(result) // 5

    _, err = divide(10, 0)
    if err != nil {
        fmt.Println("Error:", err)
    }

    err = validateAge(-5)
    var valErr *ValidationError
    if errors.As(err, &valErr) {
        fmt.Println("Campo con error:", valErr.Field)
    }
}

8. ¿Qué son las funciones `defer`, `panic` y `recover`?

go
package main

import "fmt"

func riskyOperation() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recuperado del panic:", r)
        }
    }()

    panic("algo salió muy mal")
}

func readFile() {
    fmt.Println("abriendo archivo")
    defer fmt.Println("cerrando archivo")
    fmt.Println("leyendo contenido")
}

func main() {
    readFile()
    fmt.Println("---")
    riskyOperation()
    fmt.Println("el programa continúa después del recover")
}

En la entrevista: defer es para cleanup. panic es para errores irrecuperables. recover solo funciona dentro de una función defer.


Structs, interfaces y tipos

9. ¿Cómo funcionan los structs en Go?

go
package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func (p Person) Greet() string {
    return fmt.Sprintf("Hola, soy %s", p.Name)
}

func (p *Person) Birthday() {
    p.Age++
}

func main() {
    p2 := Person{
        Name: "Bob",
        Age:  25,
    }

    type Employee struct {
        Person
        Company string
        Salary  float64
    }

    emp := Employee{
        Person:  Person{Name: "Charlie", Age: 35},
        Company: "Acme",
        Salary:  75000,
    }

    p2.Birthday()
    fmt.Println(p2.Age)  // 26
    fmt.Println(emp.Name)    // acceso directo via embedding
    fmt.Println(emp.Greet()) // método de Person disponible en Employee
}

10. ¿Cómo funcionan las interfaces en Go?

Las interfaces en Go son implícitas: un tipo implementa una interfaz automáticamente si tiene todos sus métodos.

go
package main

import (
    "fmt"
    "math"
)

type Shape interface {
    Area() float64
    Perimeter() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}

type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

func printShapeInfo(s Shape) {
    fmt.Printf("Área: %.2f, Perímetro: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    c := Circle{Radius: 5}
    r := Rectangle{Width: 4, Height: 6}

    printShapeInfo(c)
    printShapeInfo(r)

    var anything interface{} = c

    if circle, ok := anything.(Circle); ok {
        fmt.Println("Es un círculo con radio:", circle.Radius)
    }

    switch v := anything.(type) {
    case Circle:
        fmt.Println("Circle:", v.Radius)
    case string:
        fmt.Println("String:", v)
    default:
        fmt.Printf("Tipo desconocido: %T\n", v)
    }
}

11. ¿Cuál es la diferencia entre value receiver y pointer receiver?

go
package main

import "fmt"

type Counter struct {
    count int
}

func (c Counter) Get() int {
    return c.count
}

func (c *Counter) Increment() {
    c.count++
}

func main() {
    c := Counter{}
    c.Increment()
    c.Increment()
    fmt.Println(c.Get()) // 2
}

Regla: usá pointer receiver si el método modifica el struct, si el struct es grande, o para consistencia con otros métodos del mismo tipo.


12. ¿Qué es la interfaz `error` y cómo usar `fmt.Errorf` con `%w`?

go
package main

import (
    "errors"
    "fmt"
)

var ErrNotFound = errors.New("not found")

type DBError struct {
    Code    int
    Message string
    Err     error
}

func (e *DBError) Error() string {
    return fmt.Sprintf("db error %d: %s", e.Code, e.Message)
}

func (e *DBError) Unwrap() error {
    return e.Err
}

func fetchUser(id int) error {
    if id == 0 {
        return &DBError{Code: 404, Message: "usuario no encontrado", Err: ErrNotFound}
    }
    return nil
}

func getUser(id int) error {
    err := fetchUser(id)
    if err != nil {
        return fmt.Errorf("getUser(%d): %w", id, err)
    }
    return nil
}

func main() {
    err := getUser(0)
    if err != nil {
        fmt.Println(err)

        if errors.Is(err, ErrNotFound) {
            fmt.Println("el recurso no existe")
        }

        var dbErr *DBError
        if errors.As(err, &dbErr) {
            fmt.Println("código de error:", dbErr.Code)
        }
    }
}

Concurrencia

13. ¿Qué es una goroutine?

Una goroutine es una función que se ejecuta concurrentemente con el resto del programa. Es liviana (2KB de stack inicial) y el runtime de Go puede manejar millones de ellas.

go
package main

import (
    "fmt"
    "sync"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d iniciando\n", id)
    fmt.Printf("Worker %d terminado\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }

    wg.Wait()
    fmt.Println("Todas las goroutines terminaron")
}

14. ¿Qué son los channels y cómo funcionan?

go
package main

import "fmt"

func sum(s []int, ch chan int) {
    total := 0
    for _, v := range s {
        total += v
    }
    ch <- total
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}
    ch := make(chan int)

    go sum(s[:len(s)/2], ch)
    go sum(s[len(s)/2:], ch)

    x, y := <-ch, <-ch
    fmt.Println(x, y, x+y)
}

15. ¿Cuál es la diferencia entre channels con y sin buffer?

go
package main

import "fmt"

func main() {
    // Sin buffer — síncrono: el envío bloquea hasta que alguien recibe
    unbuffered := make(chan int)
    go func() {
        unbuffered <- 42
    }()
    fmt.Println(<-unbuffered)

    // Con buffer — asíncrono hasta llenar el buffer
    buffered := make(chan int, 3)
    buffered <- 1
    buffered <- 2
    buffered <- 3
    fmt.Println(<-buffered) // 1
    fmt.Println(<-buffered) // 2
    fmt.Println(<-buffered) // 3
}

16. ¿Cómo usar `select` con channels?

go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "uno"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "dos"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("Recibido de ch1:", msg1)
        case msg2 := <-ch2:
            fmt.Println("Recibido de ch2:", msg2)
        case <-time.After(3 * time.Second):
            fmt.Println("timeout")
        }
    }
}

17. ¿Qué es un mutex y cuándo usarlo?

go
package main

import (
    "fmt"
    "sync"
)

type SafeCounter struct {
    mu sync.Mutex
    v  map[string]int
}

func (c *SafeCounter) Inc(key string) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.v[key]++
}

func (c *SafeCounter) Value(key string) int {
    c.mu.RLock()
    defer c.mu.RUnlock()
    return c.v[key]
}

func main() {
    c := SafeCounter{v: make(map[string]int)}
    var wg sync.WaitGroup

    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            c.Inc("key")
        }()
    }

    wg.Wait()
    fmt.Println(c.Value("key")) // 1000
}

18. ¿Qué es un race condition y cómo detectarlo?

Detectarlo con el race detector:

bash
go run -race main.go
go test -race ./...

Solución con atomic:

go
import "sync/atomic"

var counter int64
atomic.AddInt64(&counter, 1)

19. ¿Qué es el patrón worker pool?

go
package main

import (
    "fmt"
    "sync"
)

func workerPool(numWorkers int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
    for w := 0; w < numWorkers; w++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            for job := range jobs {
                results <- job * job
            }
        }(w)
    }
}

func main() {
    const numJobs = 9
    const numWorkers = 3

    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

    var wg sync.WaitGroup
    workerPool(numWorkers, jobs, results, &wg)

    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs)

    go func() {
        wg.Wait()
        close(results)
    }()

    total := 0
    for r := range results {
        total += r
    }
    fmt.Println("Total:", total)
}

Context y cancelación

20. ¿Cómo usar `context.Context`?

go
package main

import (
    "context"
    "fmt"
    "time"
)

func doWork(ctx context.Context, name string) error {
    select {
    case <-time.After(2 * time.Second):
        fmt.Printf("%s: completado\n", name)
        return nil
    case <-ctx.Done():
        fmt.Printf("%s: cancelado — %v\n", name, ctx.Err())
        return ctx.Err()
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()

    err := doWork(ctx, "operación-1")
    if err != nil {
        fmt.Println("Error:", err) // context deadline exceeded
    }

    ctx2, cancel2 := context.WithCancel(context.Background())
    go func() {
        time.Sleep(500 * time.Millisecond)
        cancel2()
    }()

    err = doWork(ctx2, "operación-2")
    if err != nil {
        fmt.Println("Error:", err) // context canceled
    }
}

Generics (Go 1.18+)

21. ¿Qué son los generics en Go?

go
package main

import "fmt"

func Contains[T comparable](slice []T, item T) bool {
    for _, v := range slice {
        if v == item {
            return true
        }
    }
    return false
}

func Map[T, U any](slice []T, f func(T) U) []U {
    result := make([]U, len(slice))
    for i, v := range slice {
        result[i] = f(v)
    }
    return result
}

type Stack[T any] struct {
    items []T
}

func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() (T, bool) {
    if len(s.items) == 0 {
        var zero T
        return zero, false
    }
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item, true
}

func main() {
    ints := []int{1, 2, 3, 4, 5}
    fmt.Println(Contains(ints, 3)) // true

    doubled := Map(ints, func(x int) int { return x * 2 })
    fmt.Println(doubled) // [2 4 6 8 10]

    s := Stack[string]{}
    s.Push("a")
    s.Push("b")
    val, _ := s.Pop()
    fmt.Println(val) // b
}

Testing

22. ¿Cómo escribir tests en Go?

go
// calculator_test.go
package calculator

import "testing"

func TestAddTableDriven(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positivos", 2, 3, 5},
        {"negativos", -2, -3, -5},
        {"mixtos", -2, 3, 1},
        {"ceros", 0, 0, 0},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
            }
        })
    }
}
bash
go test ./...
go test -v ./...
go test -race ./...
go test -bench=. -benchmem ./...

HTTP y APIs

23. ¿Cómo construir una API REST básica en Go?

go
package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
    Age  int    `json:"age"`
}

var users = []User{
    {ID: 1, Name: "Alice", Age: 30},
    {ID: 2, Name: "Bob", Age: 25},
}

func usersHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")

    switch r.Method {
    case http.MethodGet:
        json.NewEncoder(w).Encode(users)
    case http.MethodPost:
        var newUser User
        if err := json.NewDecoder(r.Body).Decode(&newUser); err != nil {
            http.Error(w, "JSON inválido", http.StatusBadRequest)
            return
        }
        newUser.ID = len(users) + 1
        users = append(users, newUser)
        w.WriteHeader(http.StatusCreated)
        json.NewEncoder(w).Encode(newUser)
    default:
        http.Error(w, "Método no permitido", http.StatusMethodNotAllowed)
    }
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/users", usersHandler)
    log.Fatal(http.ListenAndServe(":8080", mux))
}

24. ¿Cómo implementar middleware en Go?

go
package main

import (
    "log"
    "net/http"
    "time"
)

func Logger(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
    })
}

func RequireAuth(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if token != "Bearer secret-token" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        next.ServeHTTP(w, r)
    })
}

func main() {
    hello := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello!"))
    })

    protected := Logger(RequireAuth(hello))
    http.Handle("/hello", protected)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Patrones avanzados

25. ¿Qué es el patrón functional options?

go
package main

import (
    "fmt"
    "time"
)

type Server struct {
    host    string
    port    int
    timeout time.Duration
    maxConn int
}

type Option func(*Server)

func WithHost(host string) Option {
    return func(s *Server) { s.host = host }
}

func WithPort(port int) Option {
    return func(s *Server) { s.port = port }
}

func WithTimeout(timeout time.Duration) Option {
    return func(s *Server) { s.timeout = timeout }
}

func NewServer(opts ...Option) *Server {
    s := &Server{
        host:    "localhost",
        port:    8080,
        timeout: 30 * time.Second,
        maxConn: 100,
    }
    for _, opt := range opts {
        opt(s)
    }
    return s
}

func main() {
    s := NewServer(
        WithHost("0.0.0.0"),
        WithPort(9090),
        WithTimeout(60*time.Second),
    )
    fmt.Printf("%+v\n", s)
}

26. ¿Qué es el patrón singleton en Go?

go
package main

import (
    "fmt"
    "sync"
)

type Database struct {
    connectionString string
}

var (
    instance *Database
    once     sync.Once
)

func GetDatabase() *Database {
    once.Do(func() {
        instance = &Database{connectionString: "host=localhost port=5432"}
        fmt.Println("Database inicializada")
    })
    return instance
}

27. ¿Cómo implementar el patrón pipeline con channels?

go
package main

import "fmt"

func generate(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        for _, n := range nums {
            out <- n
        }
        close(out)
    }()
    return out
}

func square(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        for n := range in {
            out <- n * n
        }
        close(out)
    }()
    return out
}

func main() {
    c := generate(1, 2, 3, 4, 5)
    sq := square(c)
    for n := range sq {
        fmt.Println(n) // 1, 4, 9, 16, 25
    }
}

Preguntas trampa que caen seguido

28. ¿Qué imprime este código?

go
package main

import "fmt"

func main() {
    x := 5
    defer fmt.Println(x) // argumento evaluado AHORA

    x = 10
    fmt.Println(x)
}
// Output:
// 10
// 5

El argumento de defer se evalúa cuando se llama defer, no cuando se ejecuta.


29. ¿Qué es el "nil interface" gotcha?

go
package main

import "fmt"

type MyError struct{ msg string }
func (e *MyError) Error() string { return e.msg }

// MAL — retorna interface con tipo no-nil pero valor nil
func badFunction(fail bool) error {
    var err *MyError = nil
    if fail {
        err = &MyError{"algo falló"}
    }
    return err // (*MyError)(nil) no es nil como interface
}

// BIEN
func goodFunction(fail bool) error {
    if fail {
        return &MyError{"algo falló"}
    }
    return nil
}

func main() {
    err := badFunction(false)
    if err != nil {
        fmt.Println("error:", err) // se imprime aunque "no hay error"
    }

    err2 := goodFunction(false)
    if err2 != nil {
        fmt.Println("error:", err2) // no se imprime, correcto
    }
}

Una interface es nil solo cuando tipo y valor son nil.


30. ¿Qué es un `goroutine leak` y cómo evitarlo?

go
package main

import (
    "context"
    "fmt"
    "time"
)

// BIEN — usar context para cancelación
func safeOperation(ctx context.Context) <-chan int {
    ch := make(chan int, 1)
    go func() {
        select {
        case <-time.After(10 * time.Second):
            ch <- 42
        case <-ctx.Done():
            fmt.Println("goroutine cancelada limpiamente")
            return
        }
    }()
    return ch
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()

    ch := safeOperation(ctx)
    select {
    case result := <-ch:
        fmt.Println("resultado:", result)
    case <-ctx.Done():
        fmt.Println("timeout:", ctx.Err())
    }
}

31. ¿Cómo funciona `copy()` con slices?

go
package main

import "fmt"

func main() {
    src := []int{1, 2, 3, 4, 5}

    dst := make([]int, len(src))
    copy(dst, src)

    dst[0] = 99
    fmt.Println(src) // [1 2 3 4 5] — sin cambios
    fmt.Println(dst) // [99 2 3 4 5]
}

32. ¿Cómo implementarías una caché LRU thread-safe?

go
package main

import (
    "container/list"
    "fmt"
    "sync"
)

type LRUCache struct {
    capacity int
    mu       sync.Mutex
    list     *list.List
    items    map[string]*list.Element
}

type entry struct {
    key   string
    value interface{}
}

func NewLRUCache(capacity int) *LRUCache {
    return &LRUCache{
        capacity: capacity,
        list:     list.New(),
        items:    make(map[string]*list.Element),
    }
}

func (c *LRUCache) Get(key string) (interface{}, bool) {
    c.mu.Lock()
    defer c.mu.Unlock()
    if elem, ok := c.items[key]; ok {
        c.list.MoveToFront(elem)
        return elem.Value.(*entry).value, true
    }
    return nil, false
}

func (c *LRUCache) Put(key string, value interface{}) {
    c.mu.Lock()
    defer c.mu.Unlock()

    if elem, ok := c.items[key]; ok {
        c.list.MoveToFront(elem)
        elem.Value.(*entry).value = value
        return
    }

    if c.list.Len() == c.capacity {
        oldest := c.list.Back()
        if oldest != nil {
            c.list.Remove(oldest)
            delete(c.items, oldest.Value.(*entry).key)
        }
    }

    e := &entry{key, value}
    elem := c.list.PushFront(e)
    c.items[key] = elem
}

func main() {
    cache := NewLRUCache(3)
    cache.Put("a", 1)
    cache.Put("b", 2)
    cache.Put("c", 3)

    v, _ := cache.Get("a")
    fmt.Println("a:", v)

    cache.Put("d", 4) // evicts "b"

    _, ok := cache.Get("b")
    fmt.Println("b existe:", ok) // false
}

33. ¿Qué son las init functions?

go
package main

import "fmt"

var config map[string]string

func init() {
    config = make(map[string]string)
    config["env"] = "production"
}

func main() {
    fmt.Println(config)
}

Se usan para registrar drivers de base de datos (import _ "github.com/lib/pq"), inicializar configuración global, o registrar providers.


34. ¿Qué es un closure en Go?

go
package main

import "fmt"

func makeCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    counter := makeCounter()
    fmt.Println(counter()) // 1
    fmt.Println(counter()) // 2

    counter2 := makeCounter()
    fmt.Println(counter2()) // 1 — independiente

    // Trap en loops — siempre hacer shadowing
    funcs := make([]func(), 3)
    for i := 0; i < 3; i++ {
        i := i // shadowing
        funcs[i] = func() { fmt.Println(i) }
    }
    for _, f := range funcs {
        f() // 0, 1, 2
    }
}

35. ¿Qué es `io.Reader` e `io.Writer`?

go
package main

import (
    "bufio"
    "bytes"
    "fmt"
    "io"
    "strings"
)

func countWords(r io.Reader) (int, error) {
    scanner := bufio.NewScanner(r)
    scanner.Split(bufio.ScanWords)
    count := 0
    for scanner.Scan() {
        count++
    }
    return count, scanner.Err()
}

func main() {
    n, _ := countWords(strings.NewReader("hola mundo como están todos"))
    fmt.Println("Palabras:", n) // 5

    src := strings.NewReader("contenido importante")
    var dst bytes.Buffer
    io.Copy(&dst, src)
    fmt.Println(dst.String())
}

Permiten escribir código que funciona con archivos, HTTP bodies, strings, buffers y sockets sin cambiar la función.


Recursos para seguir practicando

Si llegaste hasta acá, dominás la teoría. El siguiente paso es que alguien con experiencia real te haga las preguntas, te presione cuando te trabés, y te dé feedback honesto sobre tu código.

Para la entrevista de verdad:

  • Andá a [InterviewHack.ai](https://interviewhack.ai) — generá tu dossier con las preguntas específicas de la empresa y el rol que querés. Las preguntas se generan a partir de tu CV real y de quién te va a entrevistar.
  • Si tenés la entrevista en menos de 48 horas, reservá una sesión de práctica con un coach humano que ya leyó tu dossier.

El código lo sabés. Lo que falta es la presión del tiempo real y saber cómo comunicar lo que pensás mientras lo hacés.

FAQ

¿Cuáles son las preguntas de Go que más caen en entrevistas técnicas?+

Las que más caen son: diferencia entre goroutine y thread, cómo funcionan los channels (con y sin buffer), qué es un mutex y cuándo usarlo vs channels, cómo funciona el manejo de errores (sin excepciones), diferencia entre slice y array, y cómo implementar un worker pool. En empresas que usan Go para servicios de alta concurrencia, también preguntan sobre context.Context, goroutine leaks y race conditions.

¿Qué nivel de Go se espera en una entrevista para desarrollador backend?+

Para posiciones junior: tipos básicos, slices/maps, structs, interfaces, goroutines básicas y manejo de errores. Para mid: channels, select, sync.WaitGroup, sync.Mutex, patrones de concurrencia (worker pool, pipeline), context. Para senior: profiling, escape analysis, generics, optimización de memoria, diseño de APIs concurrentes, y poder explicar las decisiones de diseño del lenguaje.

¿Cuál es la diferencia entre concurrencia y paralelismo en Go?+

Concurrencia es la capacidad de manejar múltiples tareas (pueden intercalarse en un solo CPU). Paralelismo es ejecutar múltiples tareas simultáneamente en múltiples CPUs. Go siempre es concurrente; es paralelo cuando GOMAXPROCS > 1 (el default desde Go 1.5 es igual al número de CPUs). Las goroutines proveen concurrencia; el scheduler de Go decide cuándo ejecutar cada una en paralelo.

¿Cómo se detectan race conditions en Go?+

Con el race detector integrado: 'go run -race main.go' o 'go test -race ./...'. El race detector instrumenta el binario y reporta cuando dos goroutines acceden a la misma variable concurrentemente y al menos una está escribiendo. Tiene overhead (~5-10x más lento), así que se usa en desarrollo y en CI, no en producción.

¿Cuándo usar sync.Mutex vs channels en Go?+

Channels son mejores para pasar datos entre goroutines, coordinar trabajo, señalizar eventos y construir pipelines. Mutex es mejor para proteger estado compartido que múltiples goroutines leen y escriben, como una caché o un contador. La guía del equipo de Go: si el problema se expresa naturalmente como comunicación, usá channels. Si se trata de proteger una sección crítica de código, usá mutex.

¿Qué es un goroutine leak y cómo prevenirlo?+

Un goroutine leak ocurre cuando una goroutine se queda bloqueada para siempre porque nadie lee del channel que está esperando, y el programa nunca puede liberar esa memoria. Se previene usando context.Context para cancelación: cada goroutine de larga duración debe escuchar ctx.Done() con un select y retornar cuando el context se cancela. También ayuda usar channels con buffer y el patrón 'done channel' para señalizar que ya no se necesitan resultados.

¿Cómo funciona la interfaz nil gotcha en Go?+

Una interface en Go tiene dos componentes: tipo dinámico y valor dinámico. Una interface es nil solo cuando ambos son nil. Si retornás un puntero tipado nil (como *MyError(nil)) como error, la interface tiene tipo *MyError y valor nil, por lo que no es nil — la condición 'if err != nil' es verdadera aunque no haya error real. La solución: nunca retornar variables de tipo concreto como interface; retorná nil directamente cuando no hay error.

¿Qué preguntas de código suelen pedir escribir en vivo en entrevistas de Go?+

Implementar un worker pool con channels y WaitGroup, implementar una caché LRU thread-safe, escribir un servidor HTTP con middleware de logging y autenticación, implementar el patrón pipeline con channels, escribir tests table-driven para una función, y demostrar el uso correcto de context para cancelación de operaciones con timeout.

Artículos relacionados

Cómo usar el método STAR en entrevistas (con ejemplos reales)

Aprende a responder preguntas difíciles usando el método STAR en entrevistas. Consejos y ejemplos concretos para roles remotos tech de LATAM.

Cómo conseguir trabajo remoto en dólares desde LATAM: guía real

Descubre consejos concretos para conseguir trabajo remoto en dólares desde LATAM: estrategias de búsqueda, preparación y entrevista para roles tecnológicos.

Las mejores preguntas para hacerle al entrevistador al final

Descubre las mejores preguntas para hacerle al entrevistador al final, útiles para entrevistas tech remotas, diferenciándote y logrando roles en dólares.

Cómo preparar entrevistas de desarrollo sin experiencia previa

Consejos prácticos para enfrentar entrevistas de tu primer trabajo como desarrollador, incluso sin experiencia. Técnicas para destacar y convencer en cada etapa.

Preparate para tu entrevista real

Pegá el link de tu vacante: investigamos quién te entrevista y te ensayamos en vivo.

Empezar gratis →

¿Tenés entrevista próxima? Instalá el copiloto en vivo →

InterviewHack.ai

Preparate para la entrevista exacta: quién te entrevista, tu CV a medida y coach real.

Producto

VacantesRevisar CV (ATS) gratis¿Cómo suena tu inglés?¿Te pagan bien?Reporte de sueldos LATAMCursos gratisBlogCV a medidaPráctica habladaEs gratis

Empleos remotos

ReactPythonFull-StackLATAMArgentinaMéxicoVer todas →

Preparate

Práctica habladaFrontendBackendAI EngineerPor empresaVendete con tu CV

Empresa

Buscás talentoAcerca deContactoPrivacidadTérminos

© 2026 InterviewHack.ai · Tu CV es tuyo. Nunca se usa para entrenar nada. · Un producto de IA-PTY