Clarificación: cazar ambigüedades antes de implementar

Enterate del próximo post
Un mail cuando publico algo nuevo. Sin spam y te podés dar de baja cuando quieras.

Un mail cuando publico algo nuevo. Sin spam y te podés dar de baja cuando quieras.
La spec de guardar ya tiene requisitos con forma EARS y ocho criterios de aceptación, y el post anterior terminó señalando dos casos que no se pudieron escribir porque la spec no decide qué tiene que pasar en ellos. Esos dos huecos estaban anotados desde el quinto post, como preguntas abiertas. Este post trata de cómo encontrar los que nadie anotó, y de qué hacer con cada uno una vez encontrado.
La clarificación es una revisión de la spec con un solo propósito: encontrar las frases que admiten más de una lectura antes de que alguien las implemente. No es una revisión de estilo ni de completitud en general. Busca un defecto preciso, la frase que dos personas leen distinto sin darse cuenta de que la leen distinto.
Es la fase que más se saltea, porque una spec ambigua no le parece ambigua a quien la escribió. El autor sabe lo que quiso decir y completa cada hueco sin notarlo. Por eso las herramientas que adoptan el método tienden a darle un paso propio: Spec Kit la convierte en un comando, /speckit.clarify, que se corre después de escribir la spec y antes de pasar al plan técnico. El paso funciona igual sin la herramienta. Lo que importa es que exista como un momento separado, con su propia pregunta.
La lectura atenta no alcanza por sí sola, así que conviene buscar con señales concretas: formas de frase que suelen esconder una decisión sin tomar.
| Señal | Ejemplo | Qué esconde |
|---|---|---|
| Adjetivo sin unidad | "rápido", "reciente" | Un umbral que cada lector pone a su manera |
| Referencia sin referente claro | "esa URL", "la referencia" | Dos cosas distintas a las que la palabra puede apuntar |
| Lista terminada en "etc." | "título, fecha, etc." | Elementos que nadie enumeró y alguien va a inventar |
| Pasiva sin actor | "se avisa", "se valida" | Quién hace la acción, o ante quién |
| "y" u "o" entre dos casos | "no responde o no tiene título" | Dos requisitos que pueden necesitar respuestas distintas |
| Palabra común con sentido técnico | "responde", "URL" | Dónde termina el sentido diario y empieza el preciso |
Cada ambigüedad encontrada termina en uno de tres destinos. Se responde, y la respuesta pasa a ser un requisito con su criterio. Se declara como supuesto, una decisión provisoria que queda escrita para que se vea y se pueda revisar. O queda como pregunta abierta, con la consecuencia explícita de que la parte de la spec que depende de ella todavía no se puede comprobar. Lo que no debería pasar es que una ambigüedad llegue a la implementación sin ninguno de los tres, porque entonces la resuelve quien escribe el código, sin saber que la está resolviendo.
Queda una regla sobre quién responde. Las preguntas de clarificación se contestan desde la intención, no desde la implementación. Quien conoce el problema decide cuándo dos URL son la misma; quien implementa puede señalar que la pregunta existe, pero no debería cerrarla por su cuenta.
Pasarle las seis señales a la spec de guardar encuentra tres ambigüedades más, además de las dos preguntas que ya estaban anotadas. La tabla resume el recorrido y el destino de cada una.
| Dónde | Señal | Qué queda sin decidir | Destino |
|---|---|---|---|
| Primera pregunta abierta | Referencia sin referente | Si las mayúsculas o una barra final separan dos URL | Se responde con R5 |
| Segunda pregunta abierta | "o" entre dos casos | El título de una página que no tiene título | Se responde con R6 |
| Segunda pregunta abierta | "o" entre dos casos | El título de una página que no responde | Se responde con R7 |
| Contrato | Palabra con sentido técnico | Qué pasa si el texto que llega no es una URL | Queda abierta |
R4 | Palabra con sentido técnico | Si una respuesta de error cuenta como respuesta | Queda abierta |
| Contexto y objetivo | Referencia sin referente | Si "dónde los encontró" es la URL del artículo o el sitio donde apareció el enlace | Queda abierta |
Dos señales no encuentran nada, y eso también es un resultado. El único adjetivo sin unidad, "rápido", ya lo había reemplazado el sexto post por una medida, y la spec no tiene listas abiertas. Las pasivas sin actor, como "se pide" o "se avisa", tampoco esconden nada en este caso: el supuesto de una única persona por instalación ya dice quién pide y a quién se avisa. Un supuesto bien escrito resuelve ambigüedades que ni siquiera llegan a aparecer.
La primera pregunta se responde con la forma en que la web ya trata las direcciones. El dominio no distingue mayúsculas, así que EJEMPLO.com y ejemplo.com son el mismo sitio. La ruta sí puede distinguirlas, porque lo decide cada servidor, así que /A y /a pueden ser páginas distintas y se tratan como distintas. Ignorar la barra final es una decisión del proyecto, no una regla de la web: en la práctica casi siempre lleva a la misma página, y tratarla como diferencia produciría duplicados.
La segunda pregunta, al separarla en sus dos casos, muestra que no tenían la misma dificultad. Para una página sin título, se guarda la URL como título: es el único dato que siempre existe y deja el artículo reconocible. Para una página que no responde, el problema previo era qué significa "no responde", y R4 ya tenía la medida: dos segundos. Pasado ese plazo, el sistema deja de esperar y guarda con la URL como título. Los dos casos terminaron con la misma respuesta, pero se escriben como requisitos separados porque se comprueban con casos distintos y pueden cambiar por separado.
Las tres preguntas nuevas quedan abiertas a propósito. Ninguna bloquea un criterio de los que ya existen, y cerrarlas en el mismo momento en que aparecen sería repetir lo que la clarificación busca evitar: una respuesta elegida por comodidad. La tercera ni siquiera afecta a un requisito. Muestra que la ambigüedad también vive en las partes de la spec que no se comprueban, y que ahí puede orientar mal una decisión sin que ningún criterio lo detecte.
Cambian tres secciones: ## Requisitos suma tres requisitos, ## Criterios de aceptación suma cuatro criterios al final y ## Preguntas abiertas se reemplaza entera. El resto de la spec queda igual que en el post anterior.
## Requisitos
- R1. Cuando se pide guardar una URL que no estaba guardada, el sistema
deberá guardar un artículo a partir de ella.
- R2. Si se pide guardar una URL que ya fue guardada antes, entonces el
sistema deberá avisar que ya existe, sin guardar un artículo nuevo.
- R3. El sistema deberá guardar, con cada artículo, además de la URL, el
título de la página y la fecha en la que se guardó.
- R4. Cuando se pide guardar una URL y la página responde en menos de 2
segundos, el sistema deberá entregar el resultado (artículo guardado o
aviso de que ya existe) en menos de 3 segundos desde que se pide.
- R5. El sistema deberá tratar como la misma URL a dos URL que difieren
solo en mayúsculas dentro del dominio o en una barra final.
- R6. Si se pide guardar una URL que no estaba guardada y la página
responde sin título, entonces el sistema deberá guardar el artículo
usando la URL como título.
- R7. Si se pide guardar una URL que no estaba guardada y la página no
respondió al cumplirse 2 segundos desde el pedido, entonces el sistema
deberá guardar el artículo usando la URL como título.
## Criterios de aceptación
(A1 a A8, sin cambios)
### A9. Mayúsculas en el dominio y barra final (verifica R5)
Dado que https://ejemplo.com/a ya está guardada
Cuando se pide guardar https://EJEMPLO.com/a/
Entonces el resultado avisa que ya existe
Y no se crea ningún artículo nuevo
### A10. Mayúsculas en la ruta (verifica R5)
Dado que https://ejemplo.com/a ya está guardada
Cuando se pide guardar https://ejemplo.com/A
Entonces existe un artículo para cada una de las dos URL
### A11. Página sin título (verifica R6)
Dado que https://ejemplo.com/b no está guardada
Y que la página no tiene título
Cuando se pide guardarla
Entonces el artículo guardado tiene el título "https://ejemplo.com/b"
### A12. Página que no responde (verifica R7)
Dado que https://ejemplo.com/b no está guardada
Y que la página tarda 5 segundos en responder
Cuando se pide guardarla
Entonces el artículo guardado tiene el título "https://ejemplo.com/b"
## Preguntas abiertas
- ¿Qué hace el sistema si el texto que llega no es una URL?
- ¿Una página que responde con un error, como "página no encontrada",
cuenta como una página que responde?
- En el contexto, ¿"la referencia de dónde los encontró" es la URL del
artículo o el sitio donde apareció el enlace?La decisión menos obvia está en R5. La regla podría haberse metido dentro de R2, que es el único requisito que hoy compara URL, pero eso la habría dejado atada a un solo caso. Escrita como requisito ubicuo, vale en cualquier lugar donde la spec hable de "la misma URL", incluidos los requisitos que todavía no existen, y se comprueba a través del aviso de R2, como hace A9. R6 y R7 repiten la condición de R1, "una URL que no estaba guardada", por la lección del séptimo post: sin ella, los dos requisitos dirían que se guarda un artículo también cuando la URL ya existe, y chocarían con R2.
A10 es el criterio que sostiene la primera respuesta. A9 comprueba lo que la regla une; A10 comprueba lo que la regla deja separado, y sin él una implementación que ignore todas las mayúsculas pasaría todos los criterios.
La implementación de referencia del segundo post ya respondía la primera pregunta, sin saberlo: comparaba las URL normalizando todas las mayúsculas y quitando la barra final. Con esa regla, https://ejemplo.com/A y https://ejemplo.com/a son la misma URL, y A10 falla: la segunda se rechaza con un aviso de que ya existe, aunque en un sitio que distingue mayúsculas en la ruta sean dos artículos distintos. Nadie se equivocó en el sentido habitual. La regla es razonable, se parece mucho a la elegida y difiere en un caso que nadie había mirado. Sin clarificación, esa diferencia aparece el día en que alguien intenta guardar el segundo artículo y no puede.
Con el título pasa algo parecido. Un agente que implementa R3 sin la pregunta respondida tiene que decidir qué guardar cuando falta el título, y cualquier elección cabe en el texto: un título vacío, la frase "Sin título", un error que impide guardar. La tercera incluso contradice el contexto, que pide no perder artículos. La pregunta abierta tenía el valor de mostrar el hueco; respondida, además, lo cierra.
La clarificación también tiene costos. Es tiempo antes de escribir una línea de código, y rinde cada vez menos: las primeras señales encuentran los huecos que importan, y una búsqueda exhaustiva termina produciendo preguntas que nadie necesita responder. Cada respuesta, además, puede abrir preguntas nuevas: R7 decide que, pasados dos segundos, se guarda con la URL como título, y deja sin decir si el título se corrige cuando la página termina respondiendo. Y las señales encuentran ambigüedades de forma, no de fondo. Una spec puede estar escrita sin un solo adjetivo vago y pedir algo que nadie necesita.
La clarificación busca a propósito las frases que admiten dos lecturas, con señales concretas, y le da a cada una un destino escrito: respuesta, supuesto o pregunta abierta. Lo que no recibe destino lo decide más tarde quien implementa, sin saber que está decidiendo.
La spec de guardar ya tiene siete requisitos, doce criterios y tres preguntas abiertas, todo para una sola funcionalidad. El próximo post trata la pregunta que esa cuenta deja planteada: cuánta especificación es la correcta, y en qué punto agregar más empieza a costar más de lo que ahorra.