Persistencia con local storage y repository pattern

Resumen

Agregar persistencia con local storage a una aplicación de hábitos en JavaScript resuelve el problema clásico del hard reload: perder toda la información cada vez que recargas. Aprenderás a guiar la IA con prompts precisos, aplicar el repository pattern y evitar deuda técnica al hacer vibe coding.

¿Qué es el vibe coding y por qué no debes copiar código que no entiendes?

El vibe coding es delegar en la IA la escritura de código a partir de instrucciones. Suena cómodo, pero hay una regla que no puedes saltarte: nunca copies código que tú no entiendas.

Si tú no lo entiendes, tampoco lo entenderá quien revise el repositorio. Después no te vas a acordar de por qué está ahí, y eso genera deuda técnica. Por eso, antes de aceptar cualquier sugerencia del modelo, léela, cuestiónala y ajústala.

¿Qué es la deuda técnica en vibe coding? Es el costo futuro de mantener código que nadie entiende. Aparece cuando aceptas soluciones generadas por IA sin revisarlas, y crece cada vez que alguien intenta modificar ese código a ciegas.

¿Cómo funciona local storage y cuándo conviene usarlo?

El local storage es la persistencia del navegador. Guarda información en el cliente y sobrevive a recargas, así el usuario no pierde sus datos al refrescar la página [01:20].

No es la solución ideal para producción. Lo óptimo es tener una base de datos como MySQL con un backend que exponga una API. Pero mientras aprendes o mientras conectas con un equipo de backend, local storage cumple bien su papel.

Una limitación importante: local storage no guarda objetos, guarda datos en plano. Por eso necesitas serializar tus clases con un método toJSON antes de guardarlas, y deserializar al leerlas.

¿Cómo escribir prompts efectivos para que la IA genere buen código?

Entre más conceptos de arquitectura y buenas prácticas manejes, mejor vas a guiar a la IA. Un prompt vago como implementar persistencia da resultados vagos. Un prompt que especifica técnica, archivo, patrón y principios da resultados sólidos.

El código actual de tu proyecto es parte del contexto que el modelo analiza. Si tu App.js ya aplica programación orientada a objetos y repositories, la IA tenderá a seguir esas buenas prácticas. Si tu base tiene malas prácticas, las va a heredar y amplificar.

¿Qué diferencia hay entre un prompt vago y uno detallado?

En la primera iteración, con un prompt corto como implementar local storage en App.js, el modelo modificó el constructor de Habit agregando un parámetro options con createId. La lógica funcionaba, pero mezclaba la persistencia dentro de la clase de forma imperativa.

En la segunda iteración, con un prompt que pedía crear un nuevo repository siguiendo el patrón existente y respetando el principio open-closed, el resultado fue mucho más limpio: una clase LocalStorageHabitRepository separada, sin ensuciar la lógica del dominio.

¿Cómo mejoro un prompt para Cursor o Gemini? Sé específico en tres cosas: qué técnica usar (local storage, IndexDB, API), qué patrón seguir (repository, factory) y qué principios respetar (open-closed, SOLID). Menciona archivos concretos.

¿Cómo aplicar el repository pattern para separar la fuente de datos?

El repository pattern es un patrón de programación orientada a objetos que permite cambiar la fuente de datos sin tocar la lógica de negocio. Es el mismo patrón que usan ORMs como TypeORM para mapear bases de datos.

En la aplicación de hábitos ya existía un InMemoryHabitRepository. Al agregar persistencia, en lugar de reescribir esa clase, se crea otra: LocalStorageHabitRepository. Ambas cumplen el mismo contrato, y el servicio principal solo cambia cuál instancia usa.

Los beneficios concretos son:

  • Puedes migrar de memoria a local storage sin tocar el HabitService.
  • Puedes agregar IndexDB o una API REST creando un nuevo repository.
  • Cumples el principio open-closed de SOLID: abierto a extensión, cerrado a modificación.
  • El código queda más mantenible y testeable.

¿Cómo se mantiene el ID de un hábito entre sesiones?

Aquí aparece un detalle sutil. Cada vez que creas un hábito nuevo, se genera un ID aleatorio con createId. Pero cuando recargas la página y restauras los hábitos desde local storage, no quieres generar IDs nuevos, quieres conservar los originales.

La solución elegante es pasar el ID como tercer parámetro del constructor de Habit. Si viene nulo, se ejecuta createId. Si viene con valor, se respeta el ID persistido. Así la identidad del hábito sobrevive entre sesiones sin lógica extra.

¿Por qué tratar la primera versión de la IA como un draft?

Un buen patrón mental es asumir que la primera respuesta de la IA siempre es un borrador. No te quedes con ella. Léela, identifica qué te gusta y qué no, y luego itera.

En el ejemplo, la primera versión funcionaba pero mezclaba la sincronización con local storage dentro de cada acción del componente, de forma imperativa. La segunda versión movió esa lógica dentro del HabitService, de manera que cada check-in o remove sincroniza automáticamente sin tener que llamarlo manualmente.

Algunas señales de que necesitas iterar:

  1. El modelo modifica constructores de forma extraña, como agregar objetos options innecesarios.
  2. La lógica de persistencia se repite en varios lugares en lugar de centralizarse.
  3. No se respetan patrones que ya existían en tu código base.

¿Qué alternativas hay si no tienes Cursor u otro IDE con IA?

Si no cuentas con un IDE con IA integrada, puedes usar chats como Gemini, ChatGPT o Claude de Anthropic. Solo necesitas adjuntar tus archivos (App.js, index.html) para darle contexto y pegar el mismo prompt detallado.

Gemini tiene una capa gratuita amplia y sus modelos de thinking son bastante buenos. En una prueba con el mismo prompt, Gemini generó una clase base HabitRepository usando extends, creando un contrato para que todos los repositorios implementen los mismos métodos. Esa es incluso una práctica más limpia que la primera propuesta de Cursor.

Probar el mismo prompt en varios modelos (Opus, Gemini Pro, GPT) te ayuda a comparar enfoques y quedarte con el que mejor se ajusta a tu proyecto. ¿Cuál usarías tú para tu próximo refactor? Cuéntamelo en los comentarios.