<ndg> </ndg>
nonsensedevelopmentgroup.com
Side projects · arquitectura y proceso
[ndg] el nombre es una broma; el contenido no.

Software end-to-end, como side project.

Con una larga trayectoria en el sector IT, los últimos años los he pasado más en planificaciones y reuniones de producto que en el editor. Nonsense Development Group es mi laboratorio personal: cinco apps que mantengo en paralelo a mi trabajo principal, publicadas en App Store y como APK directo, cada una con su backend y su infraestructura detrás. Algunas son medio absurdas a propósito; lo que me interesa de ellas es cómo están hechas. La idea, el diseño, la arquitectura, las tecnologías y el despliegue son míos; el código lo genera Claude Code a partir de los requisitos que dejo escritos, y lo reviso y lo pruebo antes de publicarlo. Una sola persona, todas las capas.

01
Análisis
Qué problema hay y para quién, antes de abrir el editor.
02
Arquitectura
Modelo de dominio y esquema de datos. En CheckBingo esa capa no sabe que existen Vue ni SQLite.
03
Desarrollo
Diseño de componentes y procesos. Codificación y despliegue.

Proyectos

Cinco apps en producción. Cada una se eligió para practicar un patrón distinto: cliente-servidor con WebSocket, compartición con control de privacidad, offline-first, OCR en el propio dispositivo y sincronización del texto en pantalla con la voz del sistema.

VoBo · Vocabulario Borde
vobo.es ↗
// juego de palabras en español · estilo Pasapalabra

Juego de vocabulario en castellano: cada carta plantea una regla de letra (empieza por, contiene o termina en) y el jugador responde con palabras válidas. Validación tolerante (acentos, espacios, mayúsculas) y modo multijugador en tiempo real. Ejercicio sobre arquitectura cliente-servidor con comunicación bidireccional: API REST para datos y WebSocket para partidas.

Frontend
Vue 3 Ionic 8 Capacitor 8 TypeScript strict Vite 5 vue-i18n Vitest Cypress
Backend
Go PostgreSQL Redis REST + WebSocket JWT passwordless
Infra
VPS Nginx systemd App Store APK directo
highlights
  • Pipeline de normalización determinista: cada respuesta atraviesa la cadena trim → collapse whitespace → lowercase → strip accents → strip puntuación. La Ñ se preserva como letra distinta del abecedario, no se normaliza a N. La integridad del mazo se valida en carga: regla, target_letter y valid_answers deben ser coherentes.
  • Una jugada = trabajo coordinado en BBDD: cada respuesta dispara registro del intento, actualización agregada de estadísticas del jugador autenticado y, en multijugador , emisión del evento a la sala. Las stats se acumulan jugada a jugada: un cierre forzoso de la app no pierde progreso.
  • Multijugador vía WebSocket: cada partida es una sala con canal bidireccional persistente. El servidor hace broadcasting de eventos (respuesta acertada, fallida u omitida, fin de turno) a todos los conectados. Redis sostiene presencia online y estado efímero de la sala; PostgreSQL guarda lo durable.
  • Backend Go con binario único: Gin + GORM + PostgreSQL + Redis + JWT HS256 passwordless. Compilación en local y scp al VPS, detrás de Nginx con systemd. Sin Docker en producción. La concurrencia de las salas se sostiene con goroutines.
  • Desarrollo guiado por especificaciones · cómo se aplica
LoroParrot
loroparrot.com ↗
// síntesis de voz · listas compartibles

App de repetición por voz: el usuario crea un "loro", una lista de textos que la app reproduce con síntesis de voz. El modelo se articula en tres bloques (Inicio + Loro + Cierre): un texto opcional precede a cada elemento y otro lo cierra, generando combinaciones del tipo "Odio los Lunes por la mañana". Ejercicio sobre compartición de recursos con control de privacidad, autenticación passwordless por email y analítica de descargas.

Frontend
Vue 3 Capacitor 6 Vite 4 vue-i18n axios jose (JWT) native-audio
Backend
FastAPI Uvicorn SQLAlchemy PostgreSQL PyJWT Pydantic SMTP / email
Infra
VPS Nginx App Store APK directo
highlights
  • TTS con abstracción dual por plataforma: web e iOS usan Web Speech API nativa (SpeechSynthesisUtterance) con selección de voz del sistema; Android delega a un plugin Capacitor TTS nativo, porque dentro del WebView Android la API web falla. Arriba queda una sola interfaz, speak(text, options), con dos implementaciones detrás.
  • Multi-idioma a tres niveles independientes: la UI se traduce con vue-i18n (es/en), cada loro guarda su propio language en la tabla groups, y el TTS filtra las voces del dispositivo por ese código. Un loro francés se lee con voz francesa aunque la UI esté en castellano.
  • Compartición granular con tabla shared_with: cada loro tiene visibilidad public | private. Los privados se comparten escribiendo emails concretos en una tabla indexada; no hace falta que el destinatario tenga cuenta. El primer login con ese email recupera los loros recibidos. Borrado del padre arrastra texts, shared_with y downloads por cascade delete.
  • Backend FastAPI tipado de extremo a extremo: FastAPI + SQLAlchemy + Pydantic + PostgreSQL + PyJWT. Auth passwordless con código de 6 dígitos por SMTP propio; cada verification_request tiene expires_at y is_used para evitar reuso. La tabla downloads registra cada descarga; el contador agregado download_count vive en groups para queries sin joins.
  • Desarrollo guiado por especificaciones · cómo se aplica
CumuloNimbo
cumulonimbo ↗
// acrónimos y términos más utilizados en cloud · AWS + Azure + GCP · 100% offline

App en castellano para manejar el vocabulario de AWS, Azure y Google Cloud: el que aparece en su documentación y el que preguntan sus certificaciones, incluidas las de IA y machine learning. El glosario es la entidad de primer nivel: 671 términos curados a mano, y el quiz es su capa de evaluación: 1.045 preguntas tipo examen, cada una enlazada al término que la explica. Ejercicio sobre arquitectura offline-first, modelado multi-cloud con tabla de equivalencias entre servicios (S3 ↔ Blob Storage ↔ Cloud Storage), y distribución dual en App Store y APK directo.

Frontend
Vue 3 Ionic 8 Capacitor 8 TypeScript Pinia Vite 5 Sass vue-i18n
Datos
SQLite (bundled) @capacitor-community/sqlite sql.js jeep-sqlite UUID PKs
Aplicación sin conexión
Sin backend Sin red Sin telemetría Sin tracking
highlights
  • Términos utilizados en certificaciones como: AWS CLF-C02 · SAA-C03 · AIF-C01 · MLA-C01 · Azure AZ-900 · GCP ACE.
  • 5 subtipos de pregunta: definición, acrónimos, casos de uso, diferenciación entre servicios y «entre nubes» (¿qué es S3 en Azure?).
  • SQLite empaquetado en el binario: la base viaja con la app y se copia al almacenamiento del dispositivo en el primer arranque vía copyFromAssets; a partir de ahí la app no hace ninguna petición de red.
  • Distribución dual: App Store para iOS, APK directo para Android. Sin tracking ni analítica de terceros.
  • Desarrollo guiado por especificaciones · cómo se aplica
CheckBingo
checkbingo ↗
// ayudante de cartones de bingo · 3×9, 4×8 y 3×5 · OCR en el dispositivo · 100% offline

App que ayuda a no perder un cantar: el usuario fotografía sus cartones, la app los lee con el OCR del propio dispositivo y deja el resultado listo para rematarlo de un toque. Durante la partida marca los números cantados y detecta línea y bingo en todos los cartones a la vez. Ejercicio de IA aplicada: OCR nativo (Vision en iOS, ML Kit en Android) a través de un plugin Capacitor propio, y colocación en la rejilla con k-means 1D para las filas y el rango del propio número para la columna. No hay backend ni cuentas de usuario: todo ocurre en el teléfono.

Frontend
Vue 3 Ionic 8 Capacitor 8 TypeScript Pinia Vite 5 vue-i18n Vitest Playwright
Datos
SQLite @capacitor-community/sqlite offline-first arquitectura hexagonal
Visión y OCR
Vision (iOS) ML Kit (Android) plugin nativo propio k-means 1D
highlights
  • El OCR deja un borrador que el usuario corrige: el OCR nativo del sistema rellena entre un 73% y un 87% de las celdas y el resto se remata con un toque sobre la rejilla. La v1 descartó un pipeline completo con OpenCV tras diagnosticar, con una sonda instrumentada en un iPhone real, que su consumo de memoria (>1 GB) supera el límite del WebView de iOS.
  • Colocar cada número sin fiarse de la geometría: la fila sale de un k-means 1D sobre la coordenada Y; la columna, del propio número, porque cada columna tiene su rango (1-9, 10-19…). La posición horizontal solo sirve de comprobación. En la variante 3×5 densa, que no tiene rangos por columna, la columna vuelve al k-means con k = 5. El número de serie del cartón se descarta como outlier.
  • Dominio como lógica pura con arquitectura hexagonal: las reglas de cada variante (3×9 con bombo de 90, 4×8 con bombo de 80 y 3×5 densa: números por cartón, rangos por columna, detección de línea y bingo) no dependen de Vue, de SQLite ni del OCR. Más de 850 tests automáticos (Vitest + Playwright) y 12 ADRs documentando cada decisión.
  • Distribución dual verificable: App Store para iOS y APK directo para Android, con las huellas SHA-256 del fichero (CheckBingo_2.0.0.apk, 74.862.513 bytes) y del certificado de firma publicadas en la web.
  • Desarrollo guiado por especificaciones · cómo se aplica
FocusLec
focuslec ↗
// lectura en voz alta con la palabra marcada · EPUB, TXT, PDF y Word · seis idiomas · sin cuentas

App que lee documentos en voz alta y resalta la palabra que está sonando, como un karaoke. Abre EPUB, TXT, PDF y Word en seis idiomas y deja ajustar la tipografía (OpenDyslexic y Atkinson Hyperlegible entre ellas), el espaciado, el contraste y la velocidad. Desde la 1.1.0 tiene también un modo para recorrer el texto sílaba a sílaba, sin voz. La voz es la del sistema operativo, a la que se llega por una interfaz propia (TtsEngine) puesta delante del plugin de Capacitor. El proyecto se documenta desde el primer día como ejemplo del proceso: épicas, casos de uso, ADR y diagramas UML.

Frontend
Vue 3 Ionic 9 Capacitor 8 TypeScript Vite 8 vue-router Vitest
Documentos
fflate (EPUB, DOCX) pdf.js lista virtualizada @capacitor/preferences
Voz y resaltado
@capgo/capacitor-speech-synthesis TtsEngine Web Speech API SSML CMUdict keep-awake
highlights
  • Lee el documento y marca lo que suena: la voz del propio dispositivo lee documentos EPUB, TXT, PDF y Word en seis idiomas. Mientras suena, un recuadro resalta la palabra, la frase o el párrafo que se está leyendo, según se elija, y el resto del texto se puede atenuar.
  • Lectura a la medida de quien lee: tamaño de letra de 14 a 44 píxeles, interlineado, espacio entre letras y entre palabras, ancho de columna de 28 a 72 caracteres y cuatro fondos, uno de ellos en alto contraste puro. Hay cuatro tipografías, entre ellas OpenDyslexic y Atkinson Hyperlegible. La velocidad va de 0,2× a 2×. Por debajo de 0,6× la voz ya no se estira y lo que crece son las pausas entre grupos de sentido, porque al bajar la velocidad el motor alarga cada letra y deja en cero el hueco entre palabras. Junto a la velocidad se ven las palabras por minuto, medidas mientras se lee. Las opciones salen de estudios publicados sobre presentación del texto, voz sintética y dislexia; aun así, nadie especializado en dislexia o baja visión ha validado la app.
  • Sílaba a sílaba, sin voz: desde la 1.1.0 hay un modo que permite que el resaltado vaya sílaba a sílaba, en lugar de por palabras. La voz se calla y la sílaba que toca se agranda dentro del recuadro. Lo leído se queda en el color normal y lo que falta, atenuado. El avance automático va de 30 a 180 sílabas por minuto, con media sílaba más de espera al final de cada palabra, una entera tras una coma y dos al acabar la frase. Deslizando el dedo en horizontal se avanza o se retrocede (una sílaba cada 20 píxeles). Si hace falta oír algo, un toque en la palabra grande que hay encima de los botones la pronuncia. "Oír esta frase" y "Oír la anterior" leen la frase o el párrafo. Parte en sílabas los seis idiomas: el español según la Ortografía de la RAE y el inglés con el diccionario de pronunciación CMUdict.
  • Lo que hay debajo: lo que se pinta y lo que se manda al motor de voz son cadenas distintas (textoMostrado y textoNormalizado). Los avisos del motor llegan medidos sobre la segunda y la correspondencia con la primera se mantiene en unidades UTF-16.
  • Desarrollo guiado por especificaciones · cómo se aplica

Cómo se escribe el código

Desarrollo guiado por especificaciones con Claude Code.

Como apoyo para generar el código utilizo Claude Code, siguiendo el método de desarrollo guiado por especificaciones (spec-driven development, SDD). Antes de pedir una línea, los requisitos quedan escritos: qué debe hacer cada funcionalidad y con qué criterios se da por buena. Si la funcionalidad toca la arquitectura, se añade un ADR con la decisión y sus consecuencias. El agente implementa contra esos documentos y los tests comprueban que los cumple; cuando código y especificación discrepan, se corrige uno de los dos a propósito.

El vibe coding queda en el extremo opuesto. Andrej Karpathy acuñó el término en febrero de 2025 para una forma de trabajar en la que se describe lo que se quiere, se acepta lo que devuelve el modelo y, si algo falla, se vuelve a pedir, casi sin mirar el código. Para un prototipo de una tarde sirve; una app publicada en el App Store, con un backend detrás, acabaría llena de código que nadie ha leído.

Mi parte es la misma en todas: escribo los requisitos, reviso el modelo de dominio y los diagramas UML antes de implementar, acompaño la implementación y decido qué se publica. Cómo se refleja en el repositorio cambia de una app a otra. En CheckBingo, cada ticket empieza con un análisis en context/current-plan.md que se valida antes de tocar el código, y las decisiones de peso van a uno de sus 12 ADR. CumuloNimbo guarda en docs/ su modelo de datos, los diagramas UML y sus ADR. FocusLec, planteada desde el principio como ejemplo didáctico, añade un cuaderno de desarrollo a sus cuatro ADR; sus nueve diagramas están en Mermaid, guardados como texto junto a la documentación.

No se usa Spec Kit ni Kiro; las especificaciones son ficheros Markdown dentro de cada repositorio. En la clasificación de Birgitta Böckeler esto queda entre spec-first (la especificación se escribe antes) y spec-anchored (se mantiene al día mientras el código cambia). El precio es tiempo: en CheckBingo, cuyo CLAUDE.md obliga al agente a analizar antes de implementar, hasta un cambio trivial lleva su análisis escrito, aunque ocupe cuatro líneas.

Contacto

Cuéntame en cuatro líneas qué tienes en mente. Respondo en uno o dos días.