Mi App de IA Se Colgaba en una MacBook de $4,000. El Código Estaba Perfecto.
Mi app se crashea. Crash aleatorio, el peor tipo. Se muere como un pez dorado que encuentras flotando en la mañana sin explicación alguna. Builds de quince minutos en una MacBook de $4,000. El código compila. Los tests pasan. Y sin embargo: next: command not found.
Durante dos días Claude culpó a Next.js, Turbopack, Node, npm, al clima. Leí logs como un monje medieval copiando escrituras. Nada. El culpable se escondía dentro del propio macOS, en algo que ningún desarrollador piensa revisar: iCloud Drive. Cada intento de arreglo empeoraba el problema exponencialmente.
TLDR: Si tus proyectos viven en ~/Documents/, iCloud trata a node_modules como fotos de vacaciones y expulsa archivos al azar. Cada "arreglo inteligente" lo empeora. El arreglo real es un comando. Este artículo te ahorra dos días.

Y lo que más duele: mi proceso de desarrollo es sólido. Construyo con contratos de prompts que convierten el coding con IA de apostar a entregar. El código estaba probado, estructurado, documentado. Lo que significaba que el problema tenía que vivir en otro lugar completamente.
La App Que Se Negaba a Correr
Empezó un martes. El servidor de desarrollo de Next.js arrancaba, compilaba un rato, luego moría. A veces después de 30 segundos. A veces después de 10 minutos. A veces corría bien por una hora y luego se crasheaba en medio de un hot reload.
Sin patrón. Sin trigger reproducible. Solo muerte.
Turbopack se quejaba de módulos faltantes. Archivos que claramente existían según ls desaparecían en medio de la compilación. Reiniciaba, todo funcionaba, luego se crasheaba otra vez mientras probaba un componente.
Hice lo que hace cualquier desarrollador: culpé al framework. Limpié .next. Borré node_modules, corrí npm install. Revisé la config de Turbopack. Bajé versión. Subí versión. Nada ayudaba consistentemente.
Entonces, por pura desesperación, cerré VS Code y abrí Finder.
El bug no estaba en el código. Ni siquiera estaba en la terminal.
La Expulsión Invisible
Pequeños íconos de nube. En archivos dentro de node_modules.
<figure data-source="image">

iCloud Drive estaba expulsando archivos JavaScript de mis dependencias. Removiendo código fuente real del disco y reemplazándolo con placeholders de nube para "optimizar almacenamiento." Mi carpeta node_modules tratada como una galería de fotos de vacaciones: subida, luego limpiada localmente porque seguramente no necesitaba todos esos archivos en mi máquina.
¿Por qué? Mis proyectos vivían en ~/Documents/dev/. En macOS, ~/Documents/ se sincroniza con iCloud Drive por defecto. Cada archivo ahí es territorio libre.
El arreglo conocido es el patrón del sufijo .nosync:
mv node_modules node_modules.nosync
ln -s node_modules.nosync node_modules
Renombrar la carpeta real para que iCloud la ignore, crear un symlink para que Node.js la siga encontrando en la ruta esperada.
Apliqué el arreglo. El servidor reinició. El dashboard cargó.
Problema resuelto. A la mañana siguiente, todo estaba roto otra vez.
El Descenso: Cinco Arreglos Que Empeoraron Todo
Café matutino. Abro terminal. next: command not found. El symlink había desaparecido. No la carpeta, la carpeta estaba ahí, intacta. Solo el symlink se había esfumado.
Lo recreé. El servidor arrancó. Quince segundos después: crash. Symlink desaparecido.
Ahí fue cuando noté:
$ ls | grep node_modules
node_modules 3
node_modules 4
node_modules 5
node_modules 6
node_modules 7
node_modules 8
node_modules.nosync
iCloud no estaba borrando mi symlink. Lo estaba renombrando. Conflicto detectado con algún fantasma del servidor, resuelto pegándole un número a la versión local. Al final del día: node_modules 20.
Lo que sigue son cinco intentos de arreglar esto. Cada uno más ingenioso que el anterior. Cada uno empeorando las cosas. Un descenso apropiado a la locura, como una de esas historias de terror donde cada puerta que abres lleva a un cuarto más pequeño.
Arreglo 1: Limpiar las copias numeradas. Obviamente los duplicados alimentan el loop. Los remuevo, el problema desaparece. Funcionó por 20 segundos. Luego apareció node_modules 9. La entrada del servidor seguía ahí.
Arreglo 2: Atributos extendidos para excluir del sync. macOS tiene atributos extendidos (xattrs) que controlan cómo el file provider maneja archivos. Marqué el symlink como excluido:
xattr -w com.apple.bird.metadata '{"excludeFromSync":true}' node_modules
xattr -w com.apple.fileprovider.ignore 1 node_modules
Este es el arreglo que rompió todo de verdad. En lugar de desapariciones aleatorias, el symlink ahora se esfumaba en 15 segundos exactos. Cada. Maldita. Vez. Como si le hubiera pintado una diana encima.
Arreglo 3: Flag inmutable. chflags uchg hace un archivo no borrable. Seguramente nada puede sobrepasar al filesystem mismo, ¿no?
El daemon del file provider de iCloud corre con privilegios elevados. Pasó por alto el flag inmutable, creó node_modules 19, removió el original "protegido". (Tanto por eso.)
Arreglo 4: Mover node_modules completamente fuera de iCloud. Poner los archivos reales en ~/Library/ (no sincronizado), el symlink apunta ahí. Lógico.
Siguió crasheándose. A iCloud no le importa a dónde apunte un symlink. Maneja el symlink en sí porque el symlink vive en un directorio sincronizado. Target seguro, link inseguro.
Arreglo 5: Script watchdog. Un loop de bash recreando el symlink cada 2 segundos. Curita. Turbopack tira FATAL cuando node_modules desaparece en medio de la compilación. Dos segundos es una eternidad para un bundler.
Cada arreglo "inteligente" le daba a iCloud una razón más para intervenir.
<figure data-source="infographic">

El Proyecto de al Lado Funcionaba Perfecto
Después de agotar cada idea ingeniosa, hice lo que debería haber hecho primero: comparé mi proyecto roto con uno que funcionaba.
Otro proyecto estaba en la exacta misma carpeta ~/Documents/dev/. Mismo patrón .nosync + symlink. Nunca tuvo un solo conflicto.
Corrí xattr -l en ambos:
com.apple.fileprovider.dir#N: 1
com.apple.provenance:
com.apple.bird.metadata: {"excludeFromSync":true}
com.apple.fileprovider.dir#N: 1
com.apple.fileprovider.ignore: 1
com.apple.provenance:
Dos atributos extra. com.apple.bird.metadata y com.apple.fileprovider.ignore. Exactamente los que había agregado en el Arreglo 2.
El proyecto sano no tenía nada especial. Simplemente no había sido "protegido."
Un Comando: xattr -c node_modules
xattr -c node_modules
Limpiar TODOS los atributos extendidos personalizados. Dejar que iCloud trate al symlink como un archivo aburrido y sin importancia.
Pero limpiar xattrs solo no fue el final. El servidor de iCloud todavía tenía el directorio node_modules original de antes del setup .nosync. Treinta minutos después, descargó esa copia vieja y pisó mi symlink. El fantasma seguía en el servidor.
La purga completa:
mv node_modules node_modules_purge.nosync
ls -la node_modules # debería decir "No such file"
ln -s node_modules.nosync node_modules
rm -rf node_modules_purge.nosync
La espera importa. Si te la saltas, iCloud no ha sincronizado el borrado todavía. Tu symlink fresco entra en conflicto con la copia del servidor. De vuelta al loop.
Después de la purga completa: el servidor aguantó. Cinco minutos. Treinta minutos. Una hora.
Aquí está el mecanismo (Apple no va a documentar esto, así que está reverse-engineered del comportamiento): el file provider de iCloud monitorea directorios sincronizados. Un archivo con xattrs no estándar se marca como "modificado localmente." Eso dispara un ciclo de sync comparando local vs. servidor. Si el servidor tiene una entrada diferente con el mismo nombre, conflicto detectado, archivo local renombrado. El archivo renombrado sigue cargando los xattrs personalizados, lo que dispara otro sync. Loop. Sin xattrs personalizados, iCloud ve el symlink como sin cambios y lo ignora completamente.
La mejor protección contra iCloud es no atraer su atención.
Tu App de IA Es Solo Tan Buena Como el OS de Abajo
Esto es lo que me mata. Nos obsesionamos con prompt engineering, selección de modelos, benchmarks de frameworks. Comparamos Turbopack vs. Webpack como si fuera una guerra santa. Y mientras tanto el sistema operativo de abajo está silenciosamente expulsando nuestras dependencias porque piensa que node_modules es un álbum de fotos.
Las apps de IA están más expuestas a esto que las apps web regulares. Más archivos (los agentes necesitan tooling, embeddings, configs de modelos). Más churn del filesystem (ciclos de hot reload, regeneración de cache). Más cosas que parecen "carpetas grandes de cosas que iCloud debería optimizar." Si tu proyecto de IA tiene node_modules más un vector store más artifacts de modelos, iCloud ve un mise en place de archivos para manejar útilmente por ti.
Cinco reglas que me habrían ahorrado dos días:
Siempre .nosync + symlink para node_modules. Automatízalo. Un wrapper en .zshrc que corra después de cada npm install, renombre la carpeta, recree el link. No negociable si tus proyectos están en ~/Documents/.
Nunca toques xattrs en directorios de iCloud. Nada de com.apple.bird.metadata. Nada de com.apple.fileprovider.ignore. Nada de atributos ingeniosos. No protegen archivos de iCloud. Les pintan una diana encima.
Cuando rm -rf se cuelgue, usa mv con .nosync. mv node_modules old_nm.nosync es instantáneo en APFS. Sin .nosync en el nombre nuevo, iCloud empieza a subir la carpeta renombrada. Cientos de megabytes de node_modules sincronizados para nada. (Y nunca mv a /tmp, volumen diferente, dispara una copia.)
Mantén .next como directorio real. Turbopack no puede manejar symlinks para su cache. Si iCloud crea .next 2 o .next 3, borra las copias numeradas, no hagas symlink.
O simplemente múdate fuera de iCloud. mv ~/Documents/dev ~/dev. Esa ruta no se sincroniza. Opción nuclear, arreglo permanente. Probablemente lo que debería haber hecho desde el principio en lugar de pasar dos días jugando whack-a-mole con un daemon del sistema.
Dos días por el caño, del tipo donde quieres tirar tu laptop a la piscina. Y estoy sentado al sol, WiFi arrastrándose como un perro de tres patas sedado.
El código estaba sólido. El framework configurado bien. Funcionaba ayer (el famoso "funcionaba ayer"). Algún servicio del sistema construido para sincronizar las fotos patéticas de vacaciones de gente que piensa que "la nube" es almacenamiento mágico. No para desarrollo. Definitivamente no para desarrollo. Y Apple con su secretismo compulsivo sobre cómo funciona cualquier cosa bajo el capó...
El arreglo fue un comando. Pero Claude Code solo lo encontró después de quemar cinco "soluciones" que empeoraron todo. Cada intento de proteger mis archivos le dio a iCloud una razón más para atacarlos.
Si tu app corre en Mac y tus proyectos están en ~/Documents/, revisa. No tu código. No tus deps. Tus atributos extendidos. Porque macOS no fue diseñado para ti. Fue diseñado para Karen de Contabilidad.
PD: Desactivé mi propio sync de fotos de vacaciones hace mucho tiempo. Solo digo. 😏
Fuentes: La documentación de File Provider de Apple (que convenientemente no dice nada sobre el comportamiento de conflictos de xattr). El patrón .nosync está documentado por la comunidad pero no es oficialmente soportado por Apple.
(*) La portada es generada por IA. iCloud estaba ocupado expulsando la real. 🤷