Este texto describe cómo prepararse para una entrevista de trabajo como desarrollador Go (Go: ¿Qué hay Debajo del Capó y Por qué Deberías Aprenderlo como Segundo Lenguaje?), dependiendo del nivel de experiencia (Middle, Middle+ y Senior). Se incluyen ejemplos de preguntas y soluciones.

Una entrevista desarrollador Go puede ser un reto en cualquier nivel. En esta guía pretende prepararte para el éxito con ejemplos de preguntas, ejercicios prácticos y consejos.

Desarrollador Go preparándose para una entrevista, revisando código en su ordenador.
Consejos para una entrevista exitosa como desarrollador Go.

¿Qué preguntan al contratar a un desarrollador Go de nivel Middle?

La entrevista se divide en dos partes: una presentación personal y preguntas prácticas.

  • Parte 1: Presentación personal

Se te pedirá que hables sobre ti, tu experiencia en proyectos, tu formación y las tareas que has realizado.

  • Parte 2: Preguntas prácticas
  • Conocimientos generales del lenguaje Go: Se espera que conozcas los siguientes 14 puntos:
  1. Concurrencia: goroutines y canales (base del lenguaje).
  2. Sistema de tipos e interfaces (tipado estático).
  3. Gestión de memoria y punteros.
  4. Manejo de errores (error handling).
  5. Trabajo con paquetes y módulos (go mod).
  6. Pruebas (paquete testing integrado).
  7. Trabajo con cadenas de texto y UTF-8.
  8. Slices y maps (características de su implementación).
  9. Reflection y etiquetas de estructuras.
  10. Depuración y perfilado (pprof).
  11. Trabajo con archivos e IO.
  12. Contextos (context.Context).
  13. Trabajo con JSON/XML (paquetes encoding).
  14. Generación de código (go generate).
  • Solución de problemas prácticos: Deberás resolver problemas utilizando algoritmos básicos. A continuación se muestra un ejemplo:

Ejemplo: Implementar un limitador de velocidad simple en Go

Se debe implementar un limitador de velocidad que restrinja el número de solicitudes por segundo. La solución debe ser segura para la concurrencia y ocupar poco código.

Criterios de evaluación:

  1. Seguridad para la concurrencia de la implementación.
  2. Eficiencia al eliminar solicitudes obsoletas (O(1)).
  3. Funcionamiento correcto con ventanas de tiempo.
  4. Limpieza del código y uso de las expresiones idiomáticas de Go.
  5. Presencia de pruebas.

Solución (en Go):

// rate_limiter.go

package main

import (
    "sync"
    "time"
)

type RateLimiter struct {
    window   time.Duration
    maxReq   int
    reqs     []time.Time
    mutex    sync.Mutex
    timeFunc func() time.Time
}

func NewRateLimiter(window time.Duration, maxRequests int) *RateLimiter {
    return &RateLimiter{
        window:   window,
        maxReq:   maxRequests,
        reqs:     make([]time.Time, 0),
        timeFunc: time.Now,
    }
}

func (rl *RateLimiter) Allow() bool {
    rl.mutex.Lock()
    defer rl.mutex.Unlock()

    now := rl.timeFunc()
    cutoff := now.Add(-rl.window)

    // Eliminamos solicitudes obsoletas
    for i, t := range rl.reqs {
        if t.After(cutoff) {
            rl.reqs = rl.reqs[i:]
            break
        }
    }

    if len(rl.reqs) < rl.maxReq {
        rl.reqs = append(rl.reqs, now)
        return true
    }
    return false
}


// rate_limiter_test.go
package main

import (
    "sync"
    "testing"
    "time"
)

func TestRateLimiter(t *testing.T) {
    // Creamos una función de tiempo ficticia para pruebas deterministas
    fakeTime := time.Now()
    limiter := &RateLimiter{
        window:   time.Second,
        maxReq:   3,
        reqs:     make([]time.Time, 0),
        timeFunc: func() time.Time { return fakeTime },
    }

    var wg sync.WaitGroup
    allowed := 0
    denied := 0
    var mu sync.Mutex

    // Realizamos 5 solicitudes de forma concurrente
    for i := 0; i < 5; i++ {
        wg.Add(1)

        go func() {
            defer wg.Done()

            isAllowed := limiter.Allow()

            mu.Lock()
            defer mu.Unlock()
            if isAllowed {
                allowed++
                return
            }
            denied++
        }()
    }

    wg.Wait()
    if allowed != 3 || denied != 2 {
        t.Errorf("Se esperaban 3 permitidas y 2 denegadas, se obtuvieron %d permitidas y %d denegadas", allowed, denied)
    }
}
  • Experiencia en la implementación de guías de estilo: Se te preguntará sobre tus experiencias implementando guías de estilo, los objetivos de su implementación y los resultados obtenidos.
  • Conocimiento de patrones de diseño básicos: Se espera que conozcas y puedas aplicar patrones de diseño básicos en Go.
CategoríaPatrónDescripción
Patrones de concurrencia en GoWorker PoolGestión de un pool de goroutines para el procesamiento paralelo de tareas. Permite limitar el número de tareas que se ejecutan simultáneamente.
Fan-out/Fan-inDistribución del trabajo entre varios workers (fan-out) y recolección de resultados (fan-in). Útil para el procesamiento paralelo de datos.
PipelineProcesamiento secuencial de datos a través de una cadena de goroutines. Cada etapa procesa los datos y los pasa a la siguiente.
Patrones de diseño clásicos (GoF)DecoratorAñadir comportamiento a los objetos sin modificar su estructura. En Go se implementa mediante composición y embedding.
CompositeComposición de objetos en estructuras en forma de árbol. Permite trabajar de manera uniforme con objetos individuales y con sus composiciones.
AdapterConversión de la interfaz de un objeto a la interfaz esperada por el cliente. Útil para integrar interfaces incompatibles.
StrategyEncapsulación de una familia de algoritmos y su intercambiabilidad. Permite elegir el algoritmo en tiempo de ejecución.
ObserverMecanismo de suscripción para notificar cambios. En Go se implementa a través de canales.
CommandEncapsulación de una solicitud como objeto. Permite parametrizar a los clientes con diferentes solicitudes.
Patrones idiomáticos de GoFunctional OptionsForma idiomática de configuración en Go. Permite crear APIs flexibles con parámetros opcionales.

¿Qué preguntan al contratar a un desarrollador Go de nivel Middle+?

Las preguntas evaluarán tus conocimientos técnicos, experiencia, habilidades y cualidades personales.

  • Conocimiento de construcciones específicas del lenguaje: Por ejemplo, comprensión y aplicación de interfaces.
  • Uso idiomático del lenguaje: Escribir código Go de forma eficiente y legible.
  • Diseño e implementación de un pequeño servicio: Capacidad para diseñar y construir un servicio pequeño y funcional.
  • Experiencia en proyectos: Descripción de proyectos en los que has participado, tu rol y las tareas que has realizado.
  • Conocimiento de herramientas básicas: otel, gRPC, Protobuf, HTTP(s), etc.
  • Conceptos básicos de redes y transmisión de datos:
  1. Modelo OSI y TCP/IP.
  2. Protocolos de capa de transporte (TCP/UDP).
  3. Protocolos HTTP/HTTPS.
  4. gRPC y Protocol Buffers.
  5. WebSockets.
  6. Trabajo con sockets.
  7. Concurrencia en operaciones de red.
  8. Seguridad de las conexiones de red.
  • Prácticas de testing: Conocimiento y aplicación de diferentes tipos de pruebas (unitarias, funcionales, de estrés, de rendimiento, fuzzing).
  • Uso de linters: Aplicación de linters localmente y en entornos CI/CD.
  • Herramientas de perfilado y depuración: Dominio de herramientas para el análisis de rendimiento y la depuración de código.
  • Importancia de las herramientas de observabilidad: Comprensión de la importancia de implementar herramientas de observabilidad.
  • Participación en revisiones de código: Experiencia en la revisión de código de tus compañeros.
  • Principios de diseño de sistemas: Conocimiento y aplicación de los principios de diseño de sistemas.
  • Documentación: Cómo documentas tu código (¿a través del código o de una wiki?), cómo mantienes la documentación actualizada y cómo evitas la obsolescencia.
  • Escritura de código limpio y legible: Capacidad para escribir código simple y claro, sin construcciones innecesarias. Ejemplo de código limpio vs. código complejo:
package main

import (
    "fmt"
    "os"
    "strings"
)

// Ejemplo de función de un programador principiante
func ProcessDataBeginner(input string) ([]string, error) {
    // Comprobación redundante
    if input == "" {
        err := fmt.Errorf("entrada vacía")
        return nil, err
    }

    // Varias variables para una operación simple
    var result []string
    var temp string
    var i int
    for i = 0; i < len(input); i++ {
        if input[i] != ',' {
            temp += string(input[i])
        } else {
            if temp != "" {
                result = append(result, temp)
                temp = ""
            }
        }
    }
    // Lógica duplicada
    if temp != "" {
        result = append(result, temp)
    }

    // Manejo de errores incorrecto
    if len(result) == 0 {
        return result, fmt.Errorf("no hay datos")
    }

    return result, nil
}

// Ejemplo de función de un desarrollador experimentado
func ProcessDataExpert(input string) ([]string, error) {
    // Comprobación única
    if input == "" {
        return nil, fmt.Errorf("entrada vacía")
    }

    // Mínimo de variables, legibilidad
    return strings.Split(input, ","), nil
}

func main() {
    // Ejemplo de uso
    input := "go,java,python,cpp"

    // Llamada a la versión principiante
    result1, err1 := ProcessDataBeginner(input)
    fmt.Println("Versión principiante:", result1, err1)

    // Llamada a la versión experta
    result2, err2 := ProcessDataExpert(input)
    fmt.Println("Versión experta:  ", result2, err2)
}

¿Qué preguntan al contratar a un desarrollador Go de nivel Senior?

Al contratar a un desarrollador Go para una posición Senior se formulan preguntas que verifican los conocimientos técnicos, la experiencia laboral, las habilidades y las cualidades personales del candidato.

Ante todo, un Senior es un desarrollador independiente, y sus decisiones deben ser óptimas.

  • Saber diseñar la arquitectura de aplicaciones.
  • Tener experiencia trabajando con sistemas distribuidos.
  • Demostrar habilidades de depuración y perfilado.
  • Hablar sobre su experiencia: en qué proyectos participó, su rol y su aporte.
  • Conocer el conjunto básico: OpenTelemetry (OTel), gRPC, Protobuf, HTTP(s), entre otros.
  • Explicar qué prácticas de testing aplica en el trabajo (pruebas unitarias, funcionales, de estrés, de rendimiento, fuzzing).
  • Ser capaz de crear rápidamente prototipos y validar conceptos.
  • Contar cuál ha sido su grado de participación en revisiones de código de sus colegas.
  • Entender y aplicar principios de diseño de sistemas.
  • Saber —o al menos estar dispuesto a— ayudar a los compañeros y compartir su experiencia. Contar un ejemplo personal.

Los Seniors también deben aplicar activamente herramientas basadas en IA en su trabajo: agentes de IA, revisiones automáticas con modelos, asistentes de IA, generación de documentación, generación de pruebas, búsqueda de vulnerabilidades, etc.

Asimismo, en una entrevista un Senior debe demostrar amplitud de conocimientos, erudición y compromiso. Por ejemplo, explicar qué novedades aparecieron en las dos últimas versiones del lenguaje, cómo aplicarlas de manera útil o qué traerá de nuevo y práctico la próxima versión de Go.

Las respuestas muestran el grado de interés y profundidad del candidato en el área, así como su disposición y motivación para seguir desarrollándose.

Cómo prepararse para la entrevista: consejo del experto

  1. Presta atención a la concurrencia (goroutines, canales, paquete sync), interfaces, gestión de memoria y manejo de errores. Revisa las diferencias entre map y slice, y el funcionamiento de defer, panic y recover.
  2. Resuelve problemas de algoritmos y patrones concurrentes. Escribe código Go limpio teniendo en cuenta lo siguiente:
    • La complejidad asintótica en tiempo y memoria debe ser óptima para el tipo de problema que resuelvas.
    • Uso eficiente de los recursos.
    • Trabajo correcto con contextos.
    • Optimización de asignaciones (Middle+).
    • Pruebas (Middle+).
  3. Estudia el funcionamiento interno: el planificador de goroutines, el recolector de basura, los mecanismos de gestión de memoria y los principios de construcción de sistemas distribuidos y tolerantes a fallos.

Categorizado en:

Go,