1 puntos por GN⁺ 2025-06-04 | 1 comentarios | Compartir por WhatsApp
  • La verbosidad del manejo de errores en Go ha estado durante mucho tiempo entre las principales quejas de los usuarios
  • Se debatieron y probaron varias propuestas de mejora sintáctica (por ejemplo, check/handle, try, el operador ?, etc.), pero todas fueron rechazadas por falta de consenso suficiente en la comunidad
  • Entre las consideraciones principales están el amplio impacto en código, herramientas y documentación que tendría cualquier cambio del lenguaje, junto con el principio de Go de mantener su simplicidad característica
  • Debido a la claridad del enfoque actual, su facilidad para depurar y la preferencia de algunos usuarios, hay pocos argumentos convincentes para introducir un cambio sintáctico
  • No hay planes de cambiar la sintaxis del manejo de errores en el futuro previsible, y todas las propuestas relacionadas se cerrarán sin más investigación

Planteamiento del problema de la verbosidad en el manejo de errores de Go

  • Una de las quejas históricas sobre Go es que la sintaxis para manejar errores es excesivamente verbosa
  • Un ejemplo representativo es el patrón if err != nil, que aparece repetidamente en el código
  • Cuantas más llamadas a APIs requiere un programa, más evidente se vuelve este patrón, al punto de que el código de manejo de errores puede terminar ocupando más espacio que la lógica principal
  • En las encuestas anuales a usuarios, esta queja sigue apareciendo entre las más mencionadas

Consulta con la comunidad y propuestas iniciales

  • El equipo de Go ha seguido investigando mejoras para el manejo de errores, dando gran importancia a la retroalimentación de la comunidad
  • En las discusiones del proyecto Go 2 de 2018, Russ Cox resumió formalmente el núcleo del problema del manejo de errores
    • Allí apareció la propuesta del mecanismo check y handle de Marcel van Lohuizen
    • También se incluyó una comparación con lenguajes similares y una revisión de distintas alternativas
  • Aunque este enfoque realmente hacía el código más conciso, no se adoptó debido al aumento de complejidad

La propuesta de try y lo que vino después

  • En 2019 se propuso una función integrada try mucho más simplificada
    • Llevaba a código la funcionalidad de check, omitiendo handle
    • La propuesta fue criticada por ocultar el flujo de control y terminó descartándose ante la reacción negativa de la comunidad
  • Esta experiencia dejó claro el riesgo de presentar propuestas cerradas sin suficiente retroalimentación
    • Confirmó la importancia de recoger opiniones más amplias desde las primeras etapas de diseño cuando se trata de cambios grandes

Intentos adicionales y propuestas diversas

  • La comunidad siguió presentando de forma constante numerosas variantes y enfoques alternativos para el manejo de errores
    • Ian Lance Taylor organizó la situación en un umbrella issue, y se siguieron recopilando casos en el Go Wiki y en blogs
  • En 2024 surgió una propuesta para aplicar el operador ? tomado de Rust
    • En pruebas de usabilidad a pequeña escala hubo comentarios de que resultaba intuitivo, pero tampoco se logró consenso entre opiniones diversas

Estancamiento de la discusión y conclusión

  • Aunque hubo más de tres propuestas oficiales o semioficiales y cientos de propuestas de la comunidad, todas fueron rechazadas por falta de suficiente apoyo o consenso
  • Ni siquiera el grupo interno de arquitectos de Go tiene una posición unificada sobre la dirección a seguir
  • Hasta que cambie la situación o surja un consenso claro, se decidió detener por completo los intentos de modificar la sintaxis del manejo de errores

Principales argumentos a favor de mantener el enfoque actual

  • Si se hubiera incorporado azúcar sintáctica desde el diseño inicial del lenguaje, quizá no habría controversia, pero hoy existe un ecosistema acostumbrado a un estilo usado durante 15 años
  • Introducir nueva sintaxis inevitablemente genera preocupación por una brecha de estilo y pérdida de consistencia entre código antiguo y nuevo
  • También encaja con la filosofía de diseño de Go de no hacer lo mismo de varias maneras y con su énfasis en la simplicidad y la consistencia
    • Incluso la posibilidad de redeclaración con la declaración corta de variables (:=) fue un cambio secundario derivado del manejo de errores
  • La sintaxis explícita para manejar errores (mediante if) ofrece ventajas intuitivas para leer código, depurar y establecer breakpoints
  • Además, cualquier cambio en el lenguaje implica una carga considerable en términos de alcance y costo real (código, documentación, herramientas, etc.)

Mejoras alternativas y dirección futura

  • Fortalecer la biblioteca estándar (por ejemplo, con la incorporación de cmp.Or) puede ayudar a reducir parte del código repetitivo
  • Con IDEs y herramientas de desarrollo como plegado de código, autocompletado y uso de LLMs, la verbosidad puede mitigarse en cierta medida en la práctica
  • Entre grupos importantes de usuarios de Go (por ejemplo, asistentes a Google Cloud Next), predominan las posturas contrarias a la necesidad de cambiar el lenguaje
    • A medida que aumenta el uso de Go, el problema de la verbosidad tiende a sentirse menos en la práctica

Argumentos a favor de la necesidad de mejorar la sintaxis

  • Con base en la retroalimentación de usuarios, sigue existiendo demanda de mejorar la sintaxis del manejo de errores
  • Una sintaxis que no solo reduzca caracteres, sino que también aumente la claridad podría contribuir a mejorar la calidad y la seguridad del código
  • Hace falta investigación más precisa sobre el manejo de errores que cumple una función real, y no solo sobre la verificación simple de errores

Conclusión final y política futura

  • Reconociendo que hasta ahora no ha habido consenso claro ni cambios sustanciales, se declara que en el futuro previsible se detendrán todas las discusiones y propuestas de cambios sintácticos del lenguaje para el manejo de errores
  • El proceso de debate e investigación realizado hasta ahora contribuyó indirectamente a mejorar el ecosistema y los procesos de Go
  • Si en el futuro surge una definición más clara del problema y un consenso más sólido, la discusión podría reabrirse
  • Por ahora, la intención es priorizar mantener la solidez y simplicidad propias de Go

1 comentarios

 
GN⁺ 2025-06-04
Comentario de Hacker News
  • Si alguien quiere sugerir fácilmente que el equipo de Go podría haber tomado otro camino, ojalá primero revise el wiki de Go2ErrorHandlingFeedback y la búsqueda de issues en GitHub. Casi todas las ideas propuestas ya se discutieron seriamente, y como usuario que agradece el enfoque transparente del equipo de Go, me da mucho gusto usar Go todos los días

    • El borrador de diseño menciona C++, Rust y Swift, pero cuesta encontrar referencias a do-notation/for-comprehensions/monadic-let de lenguajes funcionales como Haskell, Scala u OCaml, que es lo que yo buscaba. El equipo de Go parece maestro en diseño de lenguajes, pero al final se topa con las limitaciones de un tipado estático sin polimorfismo paramétrico, como Java, y por eso no logra resolver bien el problema del manejo de errores. Creo que esto viene de la arquitectura base del lenguaje

    • Aunque el documento está escrito por gente inteligente y muy experimentada, me sorprende muchísimo que no se mencione en ningún lado una solución como las mónadas Maybe/Either de Haskell y el operador bind (do-notation). En la práctica no es algo difícil ni pedante, y aun así es una manera muy elegante y probada de propagar errores con seguridad. No entiendo por qué la comunidad de Go no lo incorporó. Agradezco que esta página exista, pero es difícil entender que hayan pasado por alto una solución tan conocida

    • Casi todos los lenguajes ofrecen distintos enfoques mejores, así que me pregunto por qué este problema resalta tanto justo en Go. No sé si simplemente no hay consenso, o si hay alguna característica propia de Go que hace que las soluciones de otros lenguajes no encajen

    • Algo que se ve seguido en las críticas a Go es que personas relativamente no expertas suelen asumir que los desarrolladores de Go entienden menos de lenguajes que ellas. En realidad, en la mayoría de los casos los desarrolladores de Go tienen mucha más experiencia y saben muchísimo más. Quien no es experto tiende a pensar que un lenguaje con más características necesariamente es mejor, pero pasa por alto que lo importante es mantener un buen equilibrio general

  • Creo que los usuarios se benefician del conservadurismo de Go al ser tan cuidadoso con agregar nuevas funciones al lenguaje. En Swift, por ejemplo, hay tantos cambios de features que aprenderlo se vuelve difícil, y hasta en Macs recientes a veces pasa que ni un proyecto simple compila. Como las palabras clave siguen aumentando y cambiando, Swift pierde continuidad de uso; en comparación, la consistencia es una fortaleza de Go

  • Una vez me tocó una situación excepcional en la que una función de Go esperaba que ocurriera un error dentro de una función interna, y si la función interna no daba error, entonces la función principal debía tratarlo como error. En esa estructura poco común tuve que ramificar con if err == nil, pero por costumbre escribí if err != nil, y como estaba demasiado habituado al patrón de siempre, tardé bastante en encontrar el fallo. Pensé que si el lenguaje ofreciera una distinción sintáctica entre el frecuente if err != nil y el raro if err == nil, se podrían evitar más errores así

    • Cada vez que uso if err == nil, le pongo un comentario // inverted para resaltar el patrón. Estaría bien que el lenguaje lo manejara automáticamente, pero por ahora así al menos se distingue mejor
    • En realidad esta es una postura en contra de cambiar la sintaxis. El patrón frecuente if err == nil { return ... } podría verse todavía más raro en el código. Mucha gente prefiere el manejo de errores actual de Go porque es claro y fácil de leer
    • Se argumenta que la misma confusión puede aparecer en patrones como if fruit != "Apple", así que en el fondo no sería un problema exclusivo del manejo de errores, sino de bifurcación de estados en general. Al final, un error también se trata como cualquier otro valor de estado
    • Existe la posibilidad de prevenir errores desde el editor o la tipografía renderizando if err != nil como si fuera un símbolo especial, para que se mezcle naturalmente con el fondo y llame menos la atención, y hacer que solo if err == nil, que es distinto, destaque más
    • También se propone que el editor podría mejorar la legibilidad mostrando algo como if err … { de forma abreviada
  • Me gusta el manejo explícito de errores en Go. Simplemente entiendo que una función o siempre tiene éxito (minimal error) o puede fallar. Si una función puede fallar, hay que manejarlo sí o sí antes de pasar al siguiente paso. En muchos lenguajes, con el manejo de excepciones, cuando ocurre un error se va lanzando por la pila hasta que algún catch lo atrapa, y al final solo te dice dónde ocurrió el error sin dar pistas realmente útiles. En Go tienes opciones claras: 1) ignorar el error 2) devolver de inmediato si hay error 3) envolver el error y agregar información útil 4) interpretar un error específico y manejarlo por rama (por ejemplo, convertirlo en 404). En Go2 me gustaría probar un tipo Result<Value, Failure> o tipos de error más específicos y enumerables. Me parece más adecuado introducir eso en Go 2 para mantener la compatibilidad con Go 1

    • Según mi experiencia, la política de manejo de errores siempre debe decidirla quien llama, y no conviene resolverla en niveles inferiores de la pila. Al final, los errores tienden a convertirse en una tarea repetitiva de envolverlos y reenviarlos hacia arriba
    • El “manejo de errores de Go” en realidad ya lo ofrecen la mayoría de los lenguajes, como los funcionales, Rust o Java, no solo JavaScript o Python. Al final, con genéricos basta para implementar el enfoque de Go en casi cualquier lenguaje. Si la comparación se queda en JS o Python, entonces solo se está hablando de un patrón bastante común
    • Se señala que justamente eso de “si una función falla, necesariamente hay que manejarlo” es el punto débil de Go. En Go en realidad sí es posible ignorar por completo un error, así que si quieres construir software realmente robusto, el enfoque de Go puede terminar siendo una debilidad
    • Opinión amarga: Go2 al final se quedará como un “laboratorio que nunca se publicará”
  • Al principio no me convencía el manejo de errores de Go, pero después de leer el post del blog errors-are-values y empezar a usar panic(err) en los lugares adecuados, terminé muy satisfecho. Para estados anómalos que el código padre no debería manejar directamente, usar panic me permitió reducir mucho las ramas de error innecesarias en el código. Esta forma de gestionar errores me ha servido bastante en el trabajo real

    • Hay una réplica de que esa lógica en realidad no logra defender el pobre manejo de errores de Go, y que aunque se mejorara, sus ventajas no desaparecerían
    • Se menciona que PHP también permite manejo de errores por niveles y supresión de errores en el sitio de llamada con el operador @, y que bash tiene técnicas de gestión como -e
    • La primera vez que vi el flujo try/catch/finally en C# me pareció novedoso, pero ahora prefiero una lógica simple como la de Go. Incluso una mayor cantidad de líneas de código (Loc) puede ser una ventaja si el flujo queda más claro
    • También se menciona que los errores basados en sum types de Rust igualmente pertenecen al paradigma de “errors are values”
  • Ante la afirmación de que la verbosidad deja de importar cuando realmente se manejan los errores, surge una respuesta juguetona preguntando si generar manualmente stack traces de verdad cuenta como “manejar” el error. Según la definición de Go, ¿entonces una excepción (exception) también estaría siendo manejada?

    • Me pregunto si decenas de líneas de stack trace de verdad son información clara. Personalmente me parece mucho más eficiente una sola línea de wrap error, y además ayuda a mantener ordenados los logs. Llevo más de 10 años usando Go y nunca he necesitado información de pila tan verbosa, incluyendo funciones del runtime
  • No me gusta que este artículo trate el problema del manejo de errores en Go solo como “la sintaxis es verbosa”. Creo que los problemas reales son más bien: 1) los errores pueden omitirse silenciosamente o ignorarse por accidente 2) no se pueden pasar o almacenar resultados de funciones tan fácilmente como valores 3) errores anidados como errors.Is encajan de forma incómoda con el sistema de tipos 4) es difícil hacer switching sobre errores 5) hay mucho uso de valores centinela en la biblioteca estándar 6) no se lleva bien con genéricos y eso crea necesidad de paquetes auxiliares

    • El 90% de los programadores profesionales de Go escriben casos de prueba para cada rama de retorno de error y así cubrir la cobertura; en lenguajes con excepciones eso no hace falta
    • No creo que sea cierto decir que en este artículo el problema principal se planteó como “It’s too verbose”. Aunque se cambie la sintaxis, la mejora de fondo no sería tan grande
    • También hay quien ve como ventaja que Go cambie tan lentamente (hasta los genéricos tardaron muchísimo)
    • Como googler, vuelvo a sentir decepción por la decisión del equipo de Go
  • En Elixir (y Erlang), las funciones normalmente devuelven una tupla {:ok, result} o {:error, description}. Gracias a la sintaxis with de Elixir, el manejo de errores puede agruparse al final del bloque y eso mejora mucho la legibilidad. Si Go incorporara algo parecido a una sentencia with, podría encadenar ejecuciones solo mientras el error sea nil y dejar un bloque manejador de errores al final, con una lectura más clara

    • Debido al problema de consenso en la comunidad, Go es lentísimo incluso para incorporar funciones valiosas tan básicas como sum types, manejo de errores o administración de paquetes. Genéricos tardaron 13 años, manejo de errores 16 años, gestión de paquetes 9 años: los cambios van demasiado lento. La cautela importa, pero duele ver que por perseguir la perfección las decisiones siempre se postergan
    • El patrón de retornos múltiples de Go a veces también se considera anómalo según la perspectiva. La crítica es que lo único que realmente puede hacerse con una función que devuelve varios tipos es asignar sus resultados a variables
  • No entiendo por qué no siguen directamente el estilo de Rust. Sobre todo ahora que ya hay genéricos, debería ser posible implementar algo parecido rápidamente. No me convence la crítica de que el operador ? de Rust fomenta ignorar errores. En la práctica, en Go hay montones de casos donde se ignora el valor de retorno de error sin que el compilador se queje. Para evitar errores de verdad, habría que forzar un retorno de tipo Result al estilo Rust. Si el problema es que por comodidad genera controversia, entonces también habría que prohibir panic, ¿no? Es una postura bastante fuerte

    • Una opinión dice que Go no puede introducir Result porque no tiene sum types y además su diseño peculiar exige zero values para todos los tipos
    • Frente a la afirmación de que una comodidad como el “operador ?” implicaría “ya no voy a usar errores envueltos”, hay una contrarespuesta: en realidad sí puede diseñarse una comodidad de ese tipo que además fomente el wrapping
    • Como desventajas de una función orientada a la comodidad (estilo Rust), se explica que el flujo de bifurcación queda oculto en una sola línea, es más difícil poner breakpoints al depurar, y se concentra de forma extrema en bubbling más que en enrich/handling; por eso Go habría descartado sintaxis de ese tipo (por ejemplo, el operador ternario)
    • Incluso si se quisiera aplicar la comparación con Rust tal cual, aparece una duda técnica: no está claro qué sería exactamente el equivalente en Go
    • También piden un ejemplo de código para entender qué se supone que se implementó “al estilo Rust” después de la llegada de los genéricos
  • Creo que un lenguaje no debe diseñarse como si fuera una lista de casillas por marcar al adoptar features al estilo Rust, sino dentro de una coherencia general. Aunque se marquen todas las casillas de la lista, eso no significa que automáticamente encaje con la esencia del lenguaje

    • Rust ha ganado la imagen de que, al avanzar con un diseño por comité (committee), su sintaxis se volvió difícil de leer y menos consistente
    • Hay quien opina que no existe algo como una “solución perfecta”
    • En la encuesta, solo el 13% respondió que el gran problema crítico de Go era el manejo de errores, así que no parece correcto decir que sea el defecto fatal único del lenguaje. Tampoco son pocos los usuarios que prefieren el estado actual. Ver resultados de la encuesta