Cuaderno de ingenieríaIngeniero de software

Software, producto y las decisiones que los conectan.

Cuando los turnos se convierten en matemáticas

Un motor con CP-SAT: reglas humanas, restricciones matemáticas y una decisión que sigue en manos de quien lo utiliza.

Leer aquí →

Otras lecturas

Nota técnica · IA aplicada

Lo que un HTTP 200 no cuenta

Una respuesta exitosa puede contener un fallo. Ese es el problema detrás de Jev Resilience.

Nota · Formación

Profundidad técnica, visión de producto

Cómo conecto arquitectura cloud y Product Ownership en mi formación.

Proyectos

Del texto al software que funciona.

Interfaces reales, código y decisiones que sostienen los productos.

01 / Producto web · Educación clínica

Diagnostique

Un juego diario de razonamiento clínico en portugués. Pistas progresivas, hasta seis intentos y un archivo de casos anteriores.

Interfaz real de Diagnostique, con la primera pista y las siguientes bloqueadas.
01Detalle de la interfaz realCapturas del 21 oct. 2026Ampliar imagen

Decisión 01

Revelar una pista también es una decisión de API.

La interfaz en React y TypeScript refleja los intentos. El backend en Fastify determina qué información puede recibir la sesión en cada paso.

La respuesta inicial incluye las pistas reveladas y el número total de pistas. La respuesta correcta y la explicación quedan fuera de ese estado.

Representación editorial del flujo implementado
Qué comprueba el test

El test de serialización compara las pistas devueltas con el subconjunto revelado y comprueba que no se incluyan la respuesta, la explicación ni una pista futura. Es evidencia del contrato en el código, no una métrica de uso.

↑ Volver a la evidencia
Archivo real de casos de Diagnostique.
02Archivo de casosCapturas del 21 oct. 2026Ampliar imagen

Decisión 02

El historial conserva su referencia.

Las definiciones del contenido tienen revisiones en SQLite. Cada publicación referencia una de ellas y conserva el contexto en el que comenzó una sesión.

Los tests comprueban que una edición crea una revisión nueva sin sustituir la definición histórica y que las publicaciones anteriores conservan su referencia.

La relación que se conserva

Los tests cubren tanto la definición histórica como el vínculo entre publicación y revisión. La captura del archivo da contexto al historial; la implementación y los tests aportan la evidencia de consistencia.

Decisión y coste. Conservar revisiones mantiene la consistencia, pero exige distinguir definición, revisión y publicación. Actualizar el contenido deja de ser una simple sustitución del registro anterior.

Casos ficticios con fines educativos.

02 / Código abierto · Fiabilidad

Jev Resilience

Un starter de Spring Boot para detectar fallos semánticos en respuestas de servicios WebFlux.

Fixture de test · no es tráfico de producción
{
  "status": "ok",
  "note": "system under maintenance"
}
01Payload utilizado en el test offline del cliente de evaluación.Ver código fuente · Se abre en otra pestaña

Decisión 01

La evaluación se integra en el flujo reactivo.

El aspecto incorpora la evaluación al Mono o Flux devuelto. El elemento continúa o se convierte en una SemanticFailureException. La llamada no es bloqueante; eso no significa que su latencia sea cero.

Fragmento de la implementación pública
return jevEvaluationService.evaluateSilentFailure(payload)
    .flatMap(score -> {
        if (score > threshold) {
            return Mono.<T>error(
                new SemanticFailureException(score, payload));
        }
        return Mono.just(item);
    });
Método screen · código real, con ajustes de formato.Ver código fuente · Se abre en otra pestaña
Test offline existente
server.enqueue(new MockResponse()
    .setResponseCode(500).setBody("boom"));

Double score = service
    .evaluateSilentFailure("irrelevant payload").block();

assertThat(score).isEqualTo(0.0);
02failsOpenWhenServerErrors · test con respuesta simulada del evaluador.Ver código fuente · Se abre en otra pestaña

Decisión 02

Continuar ante un fallo es una elección explícita.

El test configura una respuesta 500 del evaluador y espera un score de 0.0. El servicio también aplica un timeout y onErrorReturn(0.0). Ese 500 pertenece a la dependencia simulada, no al transporte del servicio original.

La implementación del fallback

El fallback evita que la indisponibilidad del evaluador se convierta en un fallo de la aplicación. Si el score supera el umbral, el aspecto emite la excepción; con el fallback, el score es 0.0.

.timeout(properties.getTimeout())
.map(response -> extractScore(response, properties.getQuestionKey()))
.doOnError(error -> log.warn(
    "Jev evaluation failed, failing open (treating payload as valid): {}",
    error.toString()))
.onErrorReturn(0.0);
Ver código fuente · Se abre en otra pestaña

Decisión y coste. El fail-open conserva la continuidad, pero puede dejar pasar un fallo semántico cuando el evaluador no está disponible. La evaluación externa también añade latencia, coste y una dependencia operativa.

Los fragmentos muestran código y fixtures reales del repositorio. No representan tráfico de producción, benchmarks ni una ejecución de tests realizada por esta página.

Vicente Damasceno

Quién escribe y construye.

Soy Vicente Damasceno, ingeniero de software. Desarrollo sistemas backend y productos web, conectando Java, Spring Boot, React y cloud con problemas de producto. Exploro la IA aplicada como parte de sistemas, teniendo en cuenta sus límites y costes.

Hitos

Cloud y producto en la misma conversación.

AWS Certified Solutions Architect – Associate
Arquitectura cloud como parte de mi formación técnica. Ver código fuente · Se abre en otra pestaña
Professional Scrum Product Owner II
Profundidad técnica junto con visión de producto y negocio. Ver código fuente · Se abre en otra pestaña

Capturas de Diagnostique