El consejo de “buen gusto” de Brian Kernighan muestra cómo el código legible ahorra tiempo, reduce errores y ayuda a equipos reales a avanzar más rápido que los trucos ingeniosos.

El nombre de Brian Kernighan aparece en lugares que muchos desarrolladores usan sin pensarlo: herramientas clásicas de Unix, el ecosistema C y décadas de escritos que enseñaron a explicar programas con claridad. Tanto si recuerdas The C Programming Language (con Dennis Ritchie), The Unix Programming Environment o sus ensayos y charlas, el hilo común es la insistencia en expresar ideas simples con limpieza.
El mejor consejo de Kernighan no depende de la sintaxis de C ni de convenciones Unix. Va de cómo leen los humanos: buscamos estructura, dependemos de los nombres, inferimos intención y nos confundimos cuando el código oculta significado tras trucos. Por eso el “gusto” por la legibilidad sigue importando cuando escribes TypeScript, Python, Go, Java o Rust.
Los lenguajes cambian. Las herramientas mejoran. Los equipos siguen lanzando funcionalidades bajo presión, y la mayor parte del código es mantenido por alguien que no fue el autor original (a menudo tu yo del futuro). La claridad es el multiplicador que hace todo eso soportable.
Esto no es un tributo al “código héroe” ni un llamado a memorizar reglas antiguas. Es una guía práctica de hábitos que hacen que el código cotidiano sea más fácil de trabajar:
La influencia de Kernighan importa porque apunta a una meta simple y amigable para equipos: escribir código que comunique. Cuando el código se lee como una explicación clara, pasas menos tiempo descifrándolo y más tiempo mejorándolo.
El “buen gusto” en código legible no tiene que ver con estilo personal, patrones llamativos o comprimir una solución en el menor número de líneas. Es el hábito de elegir la opción más simple y clara que comunique la intención de forma fiable.
Una solución con buen gusto responde a una pregunta básica para el siguiente lector: ¿Qué intenta hacer este código y por qué lo hace así? Si esa respuesta requiere ejercicios mentales, suposiciones ocultas o descifrar trucos, el código le está costando tiempo al equipo.
La mayor parte del código se lee con mucha más frecuencia de lo que se escribe. El “buen gusto” trata la lectura como la actividad primaria:
Por eso la legibilidad no es solo estética (indentación, ancho de línea o si prefieres snake_case). Eso ayuda, pero el “buen gusto” consiste principalmente en facilitar el razonamiento: nombres claros, flujo de control obvio y estructura predecible.
Un error común es optimizar por brevedad en lugar de por claridad. A veces el código más claro es un poco más largo porque hace explícitos los pasos.
Por ejemplo, compara estos dos enfoques:
La segunda versión puede añadir líneas, pero reduce la carga cognitiva necesaria para verificar la corrección. También hace que los errores sean más fáciles de aislar y los cambios más seguros.
El buen gusto es saber cuándo dejar de “mejorar” una solución con ingenio y, en su lugar, dejar la intención clara. Si un compañero puede entender el código sin pedirte un tour, has elegido bien.
El código ingenioso a menudo se siente como una victoria en el momento: menos líneas, un truco elegante, un factor “wow” en el diff. En un equipo real, ese ingenio se convierte en una factura recurrente, pagada en tiempo de incorporación, tiempo de revisión y vacilación cada vez que alguien tiene que tocar el código.
La incorporación se ralentiza. Los nuevos compañeros no solo deben aprender el producto; también deben aprender tu dialecto privado de atajos. Si entender una función requiere decodificar operadores ingeniosos o convenciones implícitas, la gente evitará cambiarla —o la cambiará con miedo.
Las revisiones se alargan y son menos fiables. Los revisores gastan energía en demostrar que el truco es correcto en lugar de evaluar si el comportamiento coincide con la intención. Peor aún, el código ingenioso es más difícil de simular mentalmente, por lo que los revisores pasan por alto casos límite que habrían visto en una versión directa.
El ingenio se compone durante:
Unos cuantos reincidentes:
17, 0.618, -1) que codifican reglas que nadie recuerda.&& / ||) que dependen de que el lector conozca reglas sutiles de evaluación.El punto de Kernighan sobre el “gusto” aparece aquí: la claridad no es cuestión de escribir más; es cuestión de hacer evidente la intención. Si una versión “inteligente” ahorra 20 segundos hoy pero cuesta 20 minutos a cada lector futuro, no es inteligente: es cara.
El “gusto” de Kernighan suele manifestarse en decisiones pequeñas y repetibles. No necesitas una gran reescritura para hacer el código más fácil de mantener: las pequeñas mejoras de claridad se acumulan cada vez que alguien examina un archivo, busca un comportamiento o arregla un bug con prisa.
Un buen nombre reduce la necesidad de comentarios y hace más difícil ocultar errores.
Apunta a nombres que revelen la intención y que coincidan con cómo habla tu equipo:
invoiceTotalCents en lugar de sum.Si un nombre te obliga a descifrarlo, está haciendo lo contrario de su trabajo.
La lectura suele ser un escaneo. La consistencia en espacios y estructura ayuda a que la vista encuentre lo importante: límites de función, condicionales y la “ruta feliz”.
Unos hábitos prácticos:
Cuando la lógica se complica, la legibilidad suele mejorar haciendo las decisiones explícitas.
Compara estos dos estilos:
// Más difícil de escanear
if (user && user.active && !user.isBanned && (role === 'admin' || role === 'owner')) {
allow();
}
// Más claro
if (!user) return deny('missing user');
if (!user.active) return deny('inactive');
if (user.isBanned) return deny('banned');
if (role !== 'admin' && role !== 'owner') return deny('insufficient role');
allow();
La segunda versión es más larga, pero se lee como una lista de verificación —y es más fácil de extender sin romper nada.
Son elecciones “pequeñas”, pero constituyen el oficio diario del código mantenible: nombres honestos, formato que guía al lector y flujo de control que evita gimnasia mental.
El estilo de claridad de Kernighan se destaca en cómo divides el trabajo en funciones y módulos. Un lector debería poder echar un vistazo a la estructura, adivinar qué hace cada parte y acertar en gran medida antes de leer los detalles.
Apunta a funciones que hagan exactamente una cosa a un determinado “nivel de zoom”. Cuando una función mezcla validación, reglas de negocio, formato e I/O, el lector tiene que mantener varios hilos en la cabeza.
Una prueba rápida: si te sorprendes escribiendo comentarios como “// ahora haz X” dentro de una función, X suele ser candidata para una función separada con un nombre claro.
Las listas largas de parámetros son un impuesto por complejidad oculto: cada sitio de llamada se convierte en un mini-archivo de configuración.
Si varios parámetros siempre viajan juntos, agrúpalos con sentido. Los objetos de opciones (o pequeñas estructuras de datos) pueden hacer los sitios de llamada autoexplicativos —si mantienes el grupo coherente y evitas volcar todo en una bolsa “misc”.
Además, prefiere pasar conceptos del dominio en lugar de primitivos. UserId es mejor que string, y DateRange mejor que (start, end) cuando esos valores tienen reglas.
Los módulos son promesas: “Todo lo que necesitas para este concepto está aquí, y el resto está en otro lugar.” Mantén los módulos lo suficientemente pequeños como para poder sostener su propósito en la cabeza y diseña límites que minimicen efectos secundarios.
Hábitos prácticos que ayudan:
Cuando necesites estado compartido, nómbralo honestamente y documenta las invariantes. La claridad no consiste en evitar la complejidad, sino en colocarla donde el lector la espera. Para más sobre mantener estos límites durante cambios, véase /blog/refactoring-as-a-habit.
El “gusto” de Kernighan aparece en cómo comentas: el objetivo no es anotar cada línea, sino reducir la confusión futura. El mejor comentario es el que previene una suposición equivocada —especialmente cuando el código es correcto pero sorprendente.
Un comentario que reitera el código (“incrementa i”) añade ruido y enseña a los lectores a ignorar los comentarios. Los comentarios útiles explican la intención, las compensaciones o las restricciones que no son obvias por la sintaxis.
# Malo: dice lo que el código ya dice
retry_count += 1
# Bueno: explica por qué se limita el reintento
retry_count += 1 # Evita prohibiciones por throttling ante fallos repetidos
Si te tienta escribir comentarios de “qué”, a menudo es señal de que el código debería ser más claro (mejores nombres, función más pequeña, flujo de control más simple). Deja que el código lleve los hechos; deja que los comentarios lleven el razonamiento.
Nada destruye la confianza más rápido que un comentario desactualizado. Si un comentario es opcional, se desalineará con el tiempo; si está equivocado, se convierte en una fuente activa de errores.
Un hábito práctico: trata las actualizaciones de comentarios como parte del cambio, no como “algo bonito de tener”. Durante las revisiones, es razonable preguntar: ¿Este comentario sigue coincidiendo con el comportamiento? Si no, actualízalo o bórralo. “Sin comentario” es mejor que “comentario equivocado”.
Los comentarios inline son para sorpresas locales. La orientación más amplia pertenece a docstrings, READMEs o notas para desarrolladores —especialmente para:
Un buen docstring dice cómo usar la función correctamente y qué errores esperar, sin narrar la implementación. Una nota breve en /docs o en un README puede capturar la historia del “por qué lo hicimos así” para que sobreviva a refactors.
La victoria silenciosa: menos comentarios, pero cada uno gana su lugar.
La mayor parte del código parece “bien” en la ruta feliz. La prueba real del gusto es qué sucede cuando faltan entradas, los servicios fallan o un usuario hace algo inesperado. Bajo estrés, el código ingenioso tiende a ocultar la verdad. El código claro hace que la falla sea obvia y recuperable.
Los mensajes de error son parte de tu producto y de tu flujo de depuración. Escríbelos como si la próxima persona que los lea estuviera cansada y de guardia.
Incluye:
Si tienes logging, añade contexto estructurado (como requestId, userId o invoiceId) para que el mensaje sea accionable sin hurgar en datos no relacionados.
Existe la tentación de “manejarlo todo” con un one-liner ingenioso o un catch-all genérico. El buen gusto consiste en elegir los pocos casos límite que importan y hacerlos visibles.
Por ejemplo, una rama explícita para “entrada vacía” o “no encontrado” suele leerse mejor que una cadena de transformaciones que produce null implícitamente en algún punto intermedio. Cuando un caso especial importa, nómbralo y ponlo al frente.
Mezclar formas de retorno (a veces un objeto, a veces una cadena, a veces false) obliga a los lectores a mantener un árbol de decisiones mental. Prefiere patrones que se mantengan consistentes:
Un manejo de fallos claro reduce las sorpresas —y la sorpresa es donde prosperan los bugs y las llamadas a medianoche.
La claridad no solo trata de lo que tú quisiste decir al escribir el código. Trata de lo que la siguiente persona espera ver cuando abre un archivo a las 16:55. La consistencia convierte “leer código” en reconocimiento de patrones: menos sorpresas, menos malentendidos, menos debates que se repiten cada sprint.
Una buena guía de equipo es corta, específica y pragmática. No intenta codificar cada preferencia; decide las preguntas recurrentes: convenciones de nombrado, estructura de archivos, patrones de manejo de errores y qué significa “hecho” para las pruebas.
El valor real es social: evita que la misma discusión se reinicie con cada PR. Cuando algo está escrito, las revisiones pasan de “prefiero X” a “acordamos X (y aquí está la razón)”. Mantenla viva y fácil de encontrar: muchos equipos la colocan en el repo (por ejemplo, /docs/style-guide.md) para que esté cerca del código.
Usa formatters y linters para cualquier cosa medible y aburrida:
Esto libera a las personas para centrarse en el significado: nombres, forma de la API, casos límite y si el código coincide con la intención.
Las reglas manuales siguen importando cuando describen decisiones de diseño —por ejemplo, “prefiere retornos tempranos para reducir anidamiento” o “una entrada pública por módulo”. Las herramientas no pueden juzgar completamente eso.
A veces la complejidad está justificada: presupuestos de rendimiento ajustados, restricciones embebidas, concurrencia compleja o comportamiento específico de plataforma. El acuerdo debería ser: las excepciones están permitidas, pero deben ser explícitas.
Un estándar simple ayuda: documenta la compensación en un comentario breve, añade un micro-benchmark o una medición cuando se cite rendimiento y aísla el código complejo detrás de una interfaz clara para que la mayor parte del código se mantenga legible.
Una buena revisión debería sentirse menos como una inspección y más como una lección corta y enfocada en “buen gusto”. Kernighan no decía que el código ingenioso sea malvado; decía que el ingenio es caro cuando otras personas tienen que convivir con él. Las revisiones son donde los equipos pueden hacer visible esa compensación y elegir la claridad a propósito.
Empieza preguntando: “¿Puede un compañero entender esto en una pasada?” Eso suele implicar mirar nombres, estructura, pruebas y comportamiento antes de entrar en micro-optimizaciones.
Si el código es correcto pero difícil de leer, trata la legibilidad como un defecto real. Sugiere renombrar variables para reflejar la intención, dividir funciones largas, simplificar el flujo de control o añadir una prueba pequeña que demuestre el comportamiento esperado. Una revisión que detecta “esto funciona, pero no sé por qué” evita semanas de confusión futura.
Un orden práctico que funciona bien:
Las revisiones se estropean cuando el feedback suena a puntuación. En lugar de “¿Por qué harías esto?”, prueba:
Las preguntas invitan a colaboración y suelen sacar a la luz restricciones que desconocías. Las sugerencias comunican dirección sin implicar incompetencia. Ese tono es como el “gusto” se propaga en un equipo.
Si quieres legibilidad consistente, no confíes en el estado de ánimo del revisor. Añade unas pocas “chequeos de claridad” a tu plantilla de revisión y a la definición de hecho. Manténlos cortos y específicos:
Con el tiempo, esto convierte las revisiones de un control de estilo en enseñanza de juicio —justo la disciplina cotidiana que Kernighan defendía.
Las herramientas LLM pueden producir código funcional rápidamente, pero “funciona” no es la barra a la que apuntaba Kernighan: comunica lo es. Si tu equipo usa un flujo de trabajo de generación (por ejemplo, construir features vía chat y iterar en código generado), vale la pena tratar la legibilidad como un criterio de aceptación de primera clase.
En plataformas como Koder.ai, donde puedes generar frontends en React, backends en Go y apps Flutter desde un prompt de chat (y luego exportar el código), aplican los mismos hábitos basados en el gusto:
La velocidad es más valiosa cuando la salida sigue siendo fácil de revisar, mantener y extender por humanos.
La claridad no es algo que “consigues” una vez. El código se mantiene legible solo si sigues empujándolo hacia un lenguaje claro a medida que cambian los requisitos. La sensibilidad de Kernighan encaja aquí: prefiere mejoras constantes y comprensibles en lugar de reescrituras heroicas o one-liners “inteligentes” que impresionan hoy y confunden el mes que viene.
La refactorización más segura es aburrida: cambios diminutos que mantienen el comportamiento idéntico. Si tienes pruebas, ejecútalas tras cada paso. Si no, añade unas pocas verificaciones centradas alrededor del área que tocas —piensa en ellas como guardarraíles temporales para poder mejorar la estructura sin miedo.
Un ritmo práctico:
Los commits pequeños también facilitan la revisión: los compañeros pueden juzgar la intención, no buscar efectos secundarios.
No tienes que purgar cada construcción “ingeniosa” de una vez. A medida que tocas código por una feature o un bugfix, cambia atajos por equivalentes directos:
Así es como la claridad gana en equipos reales: una zona mejorada a la vez, justo donde la gente ya está trabajando.
No toda la limpieza es urgente. Una regla útil: refactoriza ahora cuando el código esté cambiando activamente, sea frecuentemente malentendido o pueda causar bugs. Programa para después cuando esté estable y aislado.
Haz visible la deuda de refactorización: deja un TODO corto con contexto o crea un ticket que describa el dolor (“difícil añadir métodos de pago; la función hace 5 trabajos”). Así puedes decidir deliberadamente en lugar de dejar que el código confuso se convierta en el impuesto permanente del equipo.
Si quieres que el “buen gusto” aparezca de forma consistente, haz que sea fácil de practicar. Aquí tienes una lista de comprobación ligera que puedes reutilizar en planificación, codificación y revisión —lo bastante corta para recordarla, lo bastante específica para actuar.
Antes: process(data) hace validación, parseo, guardado y logging en un solo lugar.
Después: Divide en validateInput, parseOrder, saveOrder, logResult. La función principal se convierte en un esquema legible.
Antes: if not valid then return false repetido cinco veces.
Después: Una sección de guardias al principio (o una función de validación) que devuelve una lista clara de problemas.
Antes: x, tmp, flag2, doThing().
Después: retryCount, draftInvoice, isEligibleForRefund, sendReminderEmail().
Antes: Un bucle con tres casos especiales escondidos en el medio.
Después: Trata los casos especiales primero (o extrae helpers), luego ejecuta el bucle directo.
Elige una mejora para adoptar esta semana: “no nuevas abreviaturas”, “ruta feliz primero”, “extraer un helper por PR” o “cada mensaje de error incluye próximos pasos”. Aplica la medida durante siete días y luego conserva lo que realmente hizo la lectura más fácil.
La influencia de Kernighan tiene menos que ver con C y más con un principio duradero: el código es un medio de comunicación.
Los lenguajes y frameworks cambian, pero los equipos siguen necesitando código que sea fácil de escanear, razonar, revisar y depurar, especialmente meses después y bajo presión de tiempo.
“Buen gusto” significa elegir de forma consistente la opción más simple y clara que comunique la intención.
Una prueba útil es: ¿puede un compañero responder “qué hace esto y por qué se hace así” sin tener que decodificar trucos o depender de supuestos ocultos?
Porque la mayor parte del código se lee mucho más de lo que se escribe.
Optimizar pensando en los lectores reduce el tiempo de incorporación, la fricción en las revisiones y el riesgo de cambios incorrectos —especialmente cuando el mantenedor es “tú en el futuro” con menos contexto.
El “impuesto” aparece como:
Si la versión ingeniosa ahorra segundos ahora pero cuesta minutos cada vez que se toca, es una pérdida neta.
Los culpables más comunes incluyen:
Estos patrones suelen ocultar el estado intermedio y hacen que los casos límite sean fáciles de pasar por alto en la revisión.
Cuando reduce la carga cognitiva.
Hacer explícitos los pasos con variables nombradas (por ejemplo, validar → normalizar → calcular) facilita verificar la corrección, simplifica la depuración y hace que futuros cambios sean más seguros, incluso si añade algunas líneas.
Busca:
invoiceTotalCents en lugar de sum)Si tienes que descifrar un nombre, no está cumpliendo su función; el nombre debe reducir la necesidad de comentarios adicionales.
Prefiere ramas simples y explícitas y mantén la “ruta feliz” visible.
Tácticas útiles:
Comenta el por qué, no el qué.
Los buenos comentarios capturan intención, compensaciones, restricciones o invariantes no obvias. Evita narrar código evidente y trata las actualizaciones de comentarios como parte del cambio: los comentarios obsoletos son peores que la ausencia de comentarios.
Usa herramientas para las reglas mecánicas (formato, imports, chequeos obvios) y reserva la revisión humana para el significado.
Una guía de estilo ligera ayuda a resolver decisiones recurrentes (nombres, estructura, patrones de manejo de errores) para que las revisiones se centren en claridad y comportamiento, no en preferencias personales.
Cuando haya excepciones por rendimiento o restricciones, documenta la compensación y aísla la complejidad detrás de una interfaz limpia.