Requisitos complejos: combinaciones de patrones
Requisitos complejos
Los 5 patrones EARS no son mutuamente excluyentes. Un mismo requisito puede tener una feature opcional, ocurrir mientras un estado dura, disparado por un evento, con manejo de error. EARS permite combinarlos siguiendo un orden estable.
El orden canónico
[Where <feature>], [While <state>], [When <event>], the <system> shall <response>.
[If <unwanted condition>, then the <system> shall <response>.]
Las precondiciones van antes del sujeto en el orden: Where → While → When. Los requisitos de comportamiento no deseado (If/Then) suelen escribirse como requisitos separados asociados al primero.
Ejemplo 1: Optional feature + Event-driven
Where the user has a Premium subscription, when the user requests a data export, the export service shall generate a ZIP archive containing all the user’s data within 60 seconds.
Wheredefine la precondición de feature (suscripción Premium)Whendefine el disparador (request de exportación)- Respuesta: generar ZIP en 60 s
Si el usuario es free, este requisito no aplica. No hay que negarlo — simplemente no se evalúa.
Ejemplo 2: State-driven + Event-driven
While the device is offline, when the user submits a form, the application shall persist the submission in the local queue and shall display a “Pending sync” indicator.
Whiledefine el estado (offline)Whendefine el evento dentro de ese estado (submit)- Respuesta: persistir + mostrar indicador
Ejemplo 3: Where + While + When (los tres juntos)
Where the multi-region replication feature is enabled, while a region is marked as primary, when a write request arrives, the storage service shall replicate the write to all secondary regions before acknowledging the client.
Caso realista en sistemas distribuidos. Tres precondiciones acumulativas. Si cualquiera no aplica, el requisito no se evalúa.
Ejemplo 4: requisito principal + requisitos de error
Separá el happy path de los errores. Cada uno es su propio requisito EARS:
REQ-PAY-001 (event-driven):
When the user submits the payment form with valid card details, the payment service shall charge the card and shall record the transaction in the ledger.
REQ-PAY-002 (unwanted behaviour ligado al anterior):
If the payment gateway returns a 4xx response, then the payment service shall display the gateway’s error message to the user and shall not record the transaction.
REQ-PAY-003 (otro caso de error):
If the payment gateway times out after 30 seconds, then the payment service shall display a generic error, shall record the attempt as “pending” in the ledger, and shall enqueue a reconciliation job.
Cada caso de error es un requisito propio. No los anides dentro del happy path.
Anti-patrón: el “frankenstein” condicional
Resistí la tentación de meter toda la lógica en una sola frase:
- When the user submits the form, if the form is valid and the user is authenticated and the feature is enabled, while the system is online, then the system shall save the data, otherwise it shall show an error.
Cinco requisitos disfrazados de uno. Partilo:
+ REQ-001: Where the feature X is enabled, while the system is online, when the user submits a valid form, the service shall save the data within 2 seconds.
+ REQ-002: If the form contains validation errors, then the service shall return a 422 response with the list of field errors.
+ REQ-003: If the user is not authenticated, then the service shall return a 401 response.
+ REQ-004: If the system is offline, then the application shall queue the submission and shall display a "Pending sync" indicator.
Más texto, más claridad, más testeable.
Cuándo combinar vs cuándo separar
Combiná cuando las precondiciones son necesarias todas juntas para que el comportamiento aplique. Si alguna no aplica, el requisito completo no aplica.
Separá cuando son casos alternativos o independientes. Si pueden evaluarse por separado, cada uno merece su propio requisito.
Diagrama: árbol de decisión para combinar
flowchart TD
Q1{¿Hay precondición de feature/plan/hardware?} -->|Sí| Where[Where ...]
Q1 -->|No| Q2
Where --> Q2
Q2{¿Hay un estado del sistema relevante?} -->|Sí| While[While ...]
Q2 -->|No| Q3
While --> Q3
Q3{¿Hay un disparador puntual?} -->|Sí| When[When ...]
Q3 -->|No| Subject
When --> Subject[The <system> shall <response>]
Subject --> Q4{¿Hay condiciones de error/no deseadas?}
Q4 -->|Sí| If[Crear REQ adicional con If/Then]
Q4 -->|No| Done[Listo]
Patrón: estado compuesto
A veces el estado es la conjunción de varios:
While the user is authenticated and the workspace is in edit mode, when the user presses Ctrl+S, the editor shall persist the document and shall show a “Saved” toast for 2 seconds.
El estado compuesto está bien si las condiciones son inseparables semánticamente. Si son separables, partilas en requisitos independientes.
Patrón: respuesta condicional simple
A veces la respuesta varía según un valor. Resistí escribir un solo requisito con if/else interno:
- When the user requests a quote, the system shall return the price including tax if the user is in the EU, otherwise without tax.
Mejor partir:
+ When the user requests a quote, the pricing service shall return the price.
+ Where the user's billing address is within the EU, the pricing service shall include VAT in the returned price according to the destination country.
+ Where the user's billing address is outside the EU, the pricing service shall return the price without sales tax.
Resumen
- Combiná
Where,While,Whenen orden cuando todas son necesarias - Mantené una respuesta por requisito salvo acciones atómicas
- Separá los casos de error en requisitos
If/Thenpropios - Evitá
frankensteinscon cinco precondiciones y dos respuestas - Trazá cada requisito con un ID estable
En el siguiente capítulo cubrimos los antipatrones más frecuentes y cómo evitarlos.