wandres.dev
DISEÑO DE APIS · las guidelines

Las API Design Guidelines: la claridad manda

El documento fundacional del estilo de Swift leído como lo que realmente es: una jerarquía de prioridades con un vértice único, la claridad en el punto de uso, que decide cualquier disputa que las reglas inferiores dejen abierta, y el linaje histórico que la convirtió en infraestructura del compilador.

⏱ 18 min

Casi todos los lenguajes tienen una guía de estilo; muy pocos tienen una guía de diseño. La diferencia no es cosmética: una guía de estilo te dice dónde colocar las llaves, una guía de diseño te dice qué pregunta debe responder el nombre de un método antes de que el método exista. Las API Design Guidelines de Swift pertenecen a la segunda categoría y llevan desde 2016 gobernando no solo la biblioteca estándar, sino la forma en que el compilador traduce cada símbolo importado desde Objective-C. Leerlas como un catálogo de reglas es perder el punto. Son una jerarquía con un vértice único —la claridad en el punto de uso— y ese vértice decide cualquier disputa que las reglas inferiores dejen abierta.

🎯 Al terminar esta lección sabrás
  • Enunciar el principio rector y la jerarquía de prioridades que lo sostiene.
  • Distinguir claridad, concisión y brevedad, y explicar por qué solo la primera es un objetivo.
  • Evaluar un nombre leyendo el lugar de la llamada y nunca la declaración aislada.
  • Reconocer el linaje histórico que convirtió una convención social en infraestructura del compilador.

El vértice de la jerarquía

El documento abre con tres frases que se citan mucho y se entienden poco. La primera dice que la claridad en el punto de uso es el objetivo más importante. La segunda, que la claridad importa más que la brevedad. La tercera, que escribir un comentario de documentación es parte del diseño, no un trámite posterior.

La palabra decisiva es jerarquía. No se trata de tres virtudes que negocian entre sí, sino de un orden lexicográfico: solo cuando dos alternativas empatan en claridad pasa a contar la fluidez, y solo cuando empatan en ambas cuenta la economía de caracteres. Un diseñador que trata las tres como un promedio ponderado producirá nombres razonables la mayoría de las veces y catastróficos justo cuando más importa, porque habrá cedido claridad a cambio de elegancia en el caso difícil.

// Empate en claridad: decide la fluidez
x.insert(y, at: z)          // se lee como una frase
x.insert(y, position: z)    // correcto pero mas rigido

// No hay empate: la claridad gana sin discusion
empleados.remove(at: 3)     // elimina el elemento en el indice 3
empleados.remove(3)         // elimina el numero 3? el indice 3?

El segundo par no es una cuestión de gusto. La firma sin etiqueta es ambigua para cualquier colección cuyo elemento sea un entero, y esa ambigüedad no la resuelve el compilador: la resuelve el lector, mal, meses después. Ese es exactamente el fallo que las guías existen para prevenir.

💡
El test de las tres lecturas

Escribe la llamada, no la firma. Léela en voz alta. Si alguien que no conoce tu tipo puede describir qué hace sin abrir la definición, el nombre está bien. Si necesita el autocompletado para recordar qué significa el segundo parámetro, ya has perdido claridad y ninguna brevedad la compensa.

Claridad, concisión y brevedad no son sinónimos

Aquí vive el malentendido más caro del nivel. Las guías no piden código verboso: piden código conciso, que es cosa distinta de código breve.

La concisión mide cuánta información transporta cada símbolo; la brevedad mide cuántos caracteres ocupa. Un identificador de tres letras que obliga a leer la implementación para descifrarlo es brevísimo y nada conciso, porque su coste real no está en la línea donde se declara sino en las cien donde se lee. Un nombre largo que hace innecesario el comentario es más conciso que la abreviatura más el comentario que la explica.

// Breve y opaco: obliga a mirar la definicion
func proc(_ d: [D], _ f: Bool) -> [R]

// Conciso: cada palabra elimina una pregunta
func resultados(para documentos: [Documento],
                incluyendoBorradores: Bool) -> [Resultado]

De ahí se derivan las dos consecuencias prácticas que más discusión generan en revisiones de código. Primera: las abreviaturas están proscritas salvo las que ya son universales en el dominio, como URL, ID o min. Segunda: omite las palabras que no aportan, porque una palabra que no elimina ninguna ambigüedad no está pagando su espacio. Ambas apuntan al mismo criterio y no se contradicen; lo que se juzga siempre es la información por símbolo, no la longitud.

El ejemplo canónico de palabra que no aporta es el nombre del tipo repetido en el método. Si el parámetro ya está tipado como Elemento, decirlo otra vez en el nombre no elimina ninguna duda y además congela en la firma un detalle que quizá cambie.

// La palabra sobra: el tipo ya lo dice
func eliminarElemento(_ elemento: Elemento)
lista.eliminarElemento(x)

// La palabra falta: sin ella la llamada es ambigua
func eliminar(_ elemento: Elemento)
lista.eliminar(x)                 // correcto
func eliminar(en posicion: Indice)  // hace falta la etiqueta

Nótese la asimetría: la misma regla que suprime la palabra en el primer caso obliga a conservarla en el segundo. No es una preferencia por lo corto, sino la aplicación uniforme de un único criterio en dos situaciones distintas.

flowchart TD
A[Dos nombres candidatos] --> B{Igual claridad en el uso}
B -- No --> C[Gana el mas claro]
B -- Si --> D{Igual fluidez al leer}
D -- No --> E[Gana el que forma frase]
D -- Si --> F[Gana el mas corto]
style C fill:#a6e3a1,color:#11111b
style E fill:#89b4fa,color:#11111b
style F fill:#f9e2af,color:#11111b

El punto de uso es el tribunal

La expresión point of use traduce mal al castellano y por eso conviene precisarla: el punto de uso es la línea donde alguien invoca tu función, no la línea donde tú la escribiste. Es un cambio de perspectiva radical, porque desplaza la autoridad del autor al lector.

Un método se escribe una vez y se lee cientos. La firma se compone con el tipo receptor, las etiquetas y los argumentos concretos delante, mientras que el autor la contempla desnuda, con nombres de parámetro genéricos y sin contexto. Diseñar mirando la declaración es diseñar mirando la vista que casi nadie tendrá. Por eso la disciplina profesional consiste en escribir primero varias llamadas plausibles, incluida alguna incómoda, y derivar la firma de ellas.

// Escribe esto primero
carrito.aplicar(cupon, si: .totalSupera(50))
horario.desplazar(por: .horas(2))
imagen.escalada(a: destino, modo: .ajustar)

// Y solo entonces declara lo que haga falta
func aplicar(_ cupon: Cupon, si condicion: Condicion)

Hay un corolario poco citado y muy útil: la claridad se juzga con el contexto que el lector tendrá en esa línea, no con el que tendría leyendo el módulo entero. Un nombre puede ser perfectamente claro dentro de un archivo de doscientas líneas y volverse críptico cuando aparece en un cuerpo de función ajeno, rodeado de código que no habla de tu dominio.

De convención social a infraestructura

Este documento no nació como manifiesto estético. Nació de un problema industrial: en 2015 Swift importaba miles de símbolos de Objective-C con nombres pensados para un lenguaje donde la firma completa formaba parte del selector, y el resultado eran llamadas redundantes hasta lo cómico. La propuesta SE-0005 codificó la traducción automática, y para poder automatizarla hubo que escribir primero, de forma explícita, las reglas que hasta entonces vivían en la cabeza de los ingenieros de Cupertino.

// Antes de la traduccion automatica
color.colorWithAlphaComponent(0.5)
vista.addSubview(otra)
cadena.stringByAppendingString(sufijo)

// Despues, con las mismas APIs de siempre
color.withAlphaComponent(0.5)
vista.addSubview(otra)
cadena.appending(sufijo)

El algoritmo de traducción es más fino de lo que parece. Elimina el tipo del receptor cuando ya está implícito, promueve a preposición las partículas que introducen argumentos, convierte los métodos que devuelven una copia en participios y detecta patrones de fábrica para reducirlos a inicializadores. Todo ello obligó a describir con precisión de gramática formal reglas que hasta entonces se transmitían por imitación.

La consecuencia conceptual es notable: las guías dejaron de ser una recomendación para convertirse en la especificación de un algoritmo. Eso explica su tono inusualmente preciso, su insistencia en categorías gramaticales y su capacidad de zanjar debates que en otros lenguajes llevan años abiertos. También explica su autoridad: cuando una biblioteca de terceros las ignora, no rompe una costumbre, rompe la continuidad de todo el ecosistema en el punto exacto donde el usuario cambia de una API a otra.

🎯

Claridad primero, siempre

La jerarquía es lexicográfica, no un promedio. La fluidez solo decide cuando hay empate en claridad.

✍️

Conciso no es corto

Se mide información por símbolo. Una abreviatura opaca es breve y despilfarradora a la vez.

👀

Diseña desde la llamada

Escribe tres invocaciones plausibles antes de escribir la firma y deriva el nombre de ellas.

Por qué una guía de nombres es en realidad una teoría del significado

Lo que hace excepcional a este documento no es su contenido concreto sino su presupuesto filosófico, que rara vez se enuncia: sostiene que el significado de una API no reside en su implementación ni en su documentación, sino en el acto de lectura de quien la invoca, y que por tanto el diseñador no controla el significado de lo que escribe, solo puede aumentar o reducir la probabilidad de que el lector reconstruya el que pretendía. Es, en términos estrictos, una posición pragmatista sobre el lenguaje trasplantada a la ingeniería: el criterio de corrección de un nombre no es la correspondencia con la implementación —un nombre puede describirla con exactitud y seguir siendo pésimo— sino su rendimiento en el uso, medido en la tasa de malentendidos que produce entre lectores competentes. De ahí que la unidad de análisis sea la línea de llamada y no la declaración, y de ahí que la brevedad quede degradada a criterio de desempate: optimizar caracteres es optimizar el coste del emisor, que se paga una vez, ignorando el del receptor, que se paga en cada lectura durante toda la vida del código y crece con el número de personas que lo tocan. La consecuencia más profunda es que la claridad no es una propiedad del símbolo sino una relación entre el símbolo y una comunidad de lectores con un contexto compartido, lo que explica por qué min es aceptable y proc no pese a tener la misma longitud, por qué el mismo nombre puede ser excelente en una biblioteca de dominio y terrible en una de propósito general, y por qué las guías dedican tanto espacio a la gramática: las categorías gramaticales son el único vocabulario preexistente y ampliamente compartido para razonar sobre expectativas de significado sin tener que negociar cada caso. La enseñanza transferible excede a Swift por completo. Cuando diseñes cualquier interfaz —una función, un endpoint, un flag de línea de órdenes, un esquema de datos— el error sistemático consiste en evaluarla desde la posición privilegiada de quien ya sabe qué hace; la disciplina consiste en construir deliberadamente la posición del ignorante informado, aquel que conoce el lenguaje y el dominio pero no tu implementación, y aceptar que su lectura, no tu intención, es la que fija el significado real de lo que has publicado.

📝
Lo esencial

La jerarquía es claridad, luego fluidez, luego economía, y se aplica en ese orden estricto. La concisión mide información por símbolo y no tiene nada que ver con la longitud. El tribunal que juzga un nombre es la línea donde se invoca, no donde se declara. Y las guías tienen fuerza de infraestructura porque el compilador las ejecuta al importar Objective-C.

⚔️ Somete tres APIs al tribunal
  1. Toma tres funciones públicas de un proyecto tuyo y escribe cinco llamadas reales de cada una, sacadas de código existente.
  2. Para cada llamada anota qué pregunta necesitaría hacer un lector que no conozca el tipo receptor.
  3. Rediseña los nombres eliminando exactamente esas preguntas y verifica que no has añadido palabras que no eliminen ninguna.
  4. Busca en tu código una abreviatura y argumenta, por escrito, si pertenece al vocabulario compartido del dominio o solo al tuyo.
  5. Localiza en la biblioteca estándar un método cuyo nombre parezca redundante y reconstruye qué ambigüedad estaría eliminando.