Skip to main content
← Back to blog

Agentikas MVP — 7 agentes para codificar tu experiencia de senior

Una arquitectura agéntica de desarrollo software donde la experiencia humana se codifica en skills, schemas y nombres. Funciona — pero sin senior detrás, es la receta perfecta para deuda técnica a velocidad de IA. El MVP, los 5 agentes que ya viven, los 2 que vienen, y por qué la herramienta no reemplaza al criterio.

Foto de Igor Omilaev en Unsplash
Agentikas MVP — 7 agentes para codificar tu experiencia de senior

La pregunta que me llevó a construir esto: ¿cuánto de lo que hago como senior engineer no es escribir código sino decidir cómo se escribe?

Llevo años desarrollando software. Pasé por todas las fases: junior aprendiendo a no romper cosas, mid leyendo código de otros, senior tomando decisiones arquitectónicas sin pensarlo, lead diseñando sistemas que sobrevivan más allá de mi cabeza. Y un día, hace unos meses, una observación honesta:

El 80% de mi trabajo de senior no era escribir código. Era decidir qué se escribe, dónde, siguiendo qué patrón, con qué tests, bajo qué restricciones.

Las herramientas IA modernas (Claude, Cursor, Copilot, los SDKs de Anthropic y OpenAI con tool use, MCP, agentic loops) cambian las reglas del juego: el escribir es cada vez más barato. Pero el decidir — esa parte humana — sigue ahí.

Agentikas es mi intento de codificar el decidir. No el código, sino las decisiones que hacen que el código no apeste. Y este MVP es la primera versión funcional de esa idea: siete agentes especializados, cada uno con una skill bien definida, orquestados vía A2A, leyendo tu repo vía MCP, y encadenados en un pipeline que produce código revisable.

Este post describe el MVP en detalle, las decisiones técnicas detrás, y — honesto desde el día 1 — los riesgos. Porque si esta herramienta cae en manos sin experiencia, el resultado no es un acelerador. Es deuda técnica a velocidad de IA.


La tesis en una imagen

                  ┌──────────────────────────────────────┐
                  │           Orchestrator               │
                  │  conversacional, gestiona el flujo   │
                  └─────────────────┬────────────────────┘
                                    │ A2A (HTTP/JSON-RPC)
       ┌──────────┬─────────────────┼──────────────┬──────────────┐
       ▼          ▼                 ▼              ▼              ▼
  ┌────────┐ ┌────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐
  │Project │ │Investi-│ │   Project    │ │   Feature    │ │ Notifier │
  │Mapper  │ │gator   │ │   Explorer   │ │   Developer  │ │          │
  │ :8003  │ │ :8001  │ │  :8002       │ │   :8004      │ │  :8005   │
  └────┬───┘ └────────┘ └──────────────┘ └──────┬───────┘ └──────────┘
       │ MCP                                     │ persist
       ▼                                         ▼
  ┌──────────────┐                       ┌────────────────┐
  │ Codebase MCP │                       │ Cloudflare R2  │
  │   :9000      │                       │ agentic-       │
  └──────────────┘                       │ artifacts      │
                                         └────────────────┘
       ▲                                          ▲
       │ ro mount                                 │ S3 SDK
       │                                          │
  target repo                              persisted patches

Y dos agentes más en TODO list:

  • QA Tester (:8006) — testea, itera con el Feature Developer hasta pasar todos los tests, abre PR.
  • Project Tech Librarian (:8007) — documenta ADRs, errores recurrentes, best practices que emergen, y mejora las skills con el tiempo.

Total: 7 agentes + un servidor MCP + un orquestador conversacional. Cada uno con su rol activo (no descriptivo), cada uno con su skill markdown intercambiable, cada uno schema-validated con Zod.


Los 5 agentes del MVP actual

1. Project Mapper

Lee el repo objetivo a través de un Codebase MCP server (Model Context Protocol). Devuelve un ProjectMap estructurado: stack_summary (lo que necesita el Investigator) + project_map (lo que necesita el Tech Lead).

El detalle elegante: usa la integración nativa de MCP en el SDK de Anthropic. Cero líneas de cliente MCP escritas — el SDK descubre tools del server, los expone a Claude, gestiona el agentic loop entero.

const response = await claude.beta.messages.create(
  {
    model: "claude-sonnet-4-6",
    max_tokens: 4000,
    system: skillContent,
    mcp_servers: [
      { type: "url", url: "http://codebase-mcp:9000/mcp", name: "codebase" },
    ],
    messages: [{ role: "user", content: "Map this repo..." }],
  },
  { headers: { "anthropic-beta": "mcp-client-2025-04-04" } },
);
// Claude usa get_repo_info, list_directory, read_file dinámicamente.
// Tú solo recibes el resultado final sintetizado.

2. Investigator

Investigación externa: dado el feature + stack del proyecto, devuelve patrones canónicos, librerías estándar, pitfalls conocidos, edge cases que deben testearse.

Skill intercambiable según el flow: feature-research.md para features nuevas, upgrade-research.md para upgrades de versión, security-research.md cuando lleguen flujos de seguridad. Mismo agente, distinta misión.

3. Project Explorer (Tech Lead)

El que decide. Recibe research + project_map y produce un DevelopmentBrief: file paths concretos, follow_pattern apuntando a un archivo del repo que sirva de referencia, test_scenarios numerados, constraints (max_files, forbidden patterns), acceptance_criteria, out_of_scope.

Es el agente más cargado de "experiencia senior codificada". Su prompt no es "audita el código" — es "tú eres tech lead; produce un brief que un junior pueda ejecutar sin pedir aclaraciones". El cambio de framing del nombre ("Auditor" → "Tech Lead") cambia los outputs del modelo más que cualquier prompt elaborado.

{
  "feature_summary": "Magic-link auth: email → token → session",
  "files_to_create": [
    { "path": "src/auth/magic-link.ts", "purpose": "core token logic" }
  ],
  "follow_pattern": {
    "reference_file": "src/auth/oauth.ts",
    "why": "imitate error handling, naming, session integration"
  },
  "test_scenarios": [
    { "id": "t1", "name": "valid email generates token", "type": "unit" },
    { "id": "t2", "name": "expired token rejected", "type": "unit" }
  ],
  "constraints": {
    "max_files_changed": 8,
    "test_framework": "vitest",
    "forbidden_patterns": ["any", "ts-ignore"]
  },
  "out_of_scope": ["Refactoring legacy auth code"]
}

4. Feature Developer

Ejecuta el brief con TDD: tests primero, código después. Output es un patch estructurado (lista de archivos con contenido) — no escribe a disco.

¿Por qué no escribe? Por sandboxing. La escritura real es trabajo de un componente futuro (Applier) con permisos restringidos. Por ahora, el output viaja como JSON y se persiste en Cloudflare R2 (bucket agentic-artifacts) para que un humano o el QA Tester futuro lo aplique.

5. Notifier

El más simple. Sin LLM. Recibe subject + body + recipients y entrega vía email/Slack/webhook (en walking skeleton: log a stdout). Existe porque separar entrega de razonamiento mantiene a los agentes cognitivos limpios. Cuando un día queramos cambiar de SMTP a SendGrid o añadir Slack, tocamos un solo agente, no cinco.

+ Codebase MCP server

No es un agente — es infraestructura. Expone el repo objetivo como Model Context Protocol server con 4 tools: list_directory, read_file, search (ripgrep), get_repo_info. Sandboxed (todo path verificado contra REPO_ROOT), execFile (no shell — anti-inyección), StreamableHTTP transport.

Lo crítico: no sabe que existe A2A. Es un MCP server estándar que cualquier cliente MCP puede consumir (Claude Desktop, otro IDE, otro agente). El Project Mapper lo usa internamente sin que el resto del sistema lo sepa. A2A coordina; MCP da capacidades. Composición ortogonal.


Los 2 agentes pendientes

QA Tester (próximo paso)

El que cierra el loop de calidad. Recibe el patch del Feature Developer y:

  1. Recompone el filesystem en un sandbox aislado
  2. Ejecuta: type-check, lint, suite existente + tests nuevos del patch
  3. Si todo pasa: hace git checkout -b feat/<slug>, commit, push, abre PR vía GitHub API
  4. Si algo falla: devuelve state: input-required al Feature Developer con un payload estructurado:
{
  "iteration": 2,
  "failures": [
    {
      "test_id": "t3",
      "error": "Expected 'expired' but got 'invalid'",
      "file": "src/auth/__tests__/magic-link.test.ts:45"
    }
  ],
  "suggestions": [
    "Token expiration check at line 23 should compare with Date.now() in ms"
  ]
}

El Feature Developer recibe esto, ajusta su patch, devuelve nuevo intento. Loop limitado a 3-5 iteraciones. Después → state: input-required al humano. Sin límite, los agentes pueden iterar indefinidamente con micro-fixes que nunca convergen.

Esto requiere:

  • Sandbox real (Docker-in-Docker o runner dedicado)
  • Token Git con permisos repo
  • GitHub API SDK (@octokit/rest)

Project Tech Librarian

El más interesante a largo plazo. Su trabajo:

  • Lee históricos de R2 (todos los briefs, patches, test reports pasados)
  • Detecta patrones recurrentes: errores que el QA Tester rechaza repetidamente, decisiones arquitectónicas similares en briefs distintos, best practices que el Tech Lead aplica consistentemente
  • Genera ADRs (Architecture Decision Records) cuando una decisión se repite N veces
  • Documenta antipatrones cuando un mismo bug aparece en M tasks
  • Propone mejoras a los markdown de skills cuando una skill produce outputs mediocres reiteradamente

Es el agente que convierte el sistema de estático a adaptativo. Sin Librarian, las skills son código humano que hay que mantener manualmente. Con Librarian, el sistema mejora con cada ciclo — cada PR mergeado es un entrenamiento para el siguiente.

Schema-as-policy: el LibrarianReport exige evidence_artifacts: string[] con mínimo 2 entradas. No genera docs sin evidencia real de tasks pasados que respalden el aprendizaje. Eso evita que invente "best practices" que suenan bien pero no están validadas por el uso real del sistema.


La ingeniería detrás: monorepo, workspaces, Docker

Estructura del repo

agentic-architecture/
├── package.json              ← root con workspaces declarados
├── package-lock.json         ← lockfile único
├── Dockerfile.workspace      ← UN dockerfile que sirve a todos los services
├── docker-compose.yml
├── .dockerignore
│
├── shared/                   ← package "@agentikas/shared"
│   ├── package.json
│   ├── a2a-client.ts         ← cliente A2A reutilizable
│   ├── artifact-store.ts     ← LocalStore + R2Store
│   └── local-llm.ts          ← wrapper Ollama OpenAI-compat
│
├── orchestrator/             ← package
│   ├── package.json          ← depende de "@agentikas/shared"
│   ├── tsconfig.json
│   └── src/server.ts
│
├── agents/
│   ├── investigator/         ← package
│   ├── notifier/
│   ├── project-explorer/
│   ├── project-mapper/
│   └── feature-developer/
│
├── services/
│   └── codebase-mcp/         ← MCP server, no es agente
│
├── skills/                   ← markdown, montados read-only en containers
│   ├── feature-research.md
│   ├── feature-briefing.md
│   ├── feature-development.md
│   ├── project-mapping.md
│   └── upgrade-research.md
│
├── workspace/                ← LocalStore (gitignored)
└── blog/                     ← markdown de los posts (este incluido)

npm workspaces: hoisting + symlinks

El package.json raíz declara:

{
  "name": "agentic-architecture",
  "workspaces": ["shared", "orchestrator", "agents/*", "services/*"]
}

npm install en raíz:

  1. Instala TODAS las deps de TODOS los packages en /node_modules raíz (hoisting)
  2. Symlinkea cada workspace package en el node_modules de cada consumidor
  3. Resultado: un solo lockfile, deps deduplicadas, agentes importan from "@agentikas/shared/local-llm" en lugar de from "../../../shared/local-llm.js"

Sin workspaces (lo que tenía antes): cada agente con su propio package.json + node_modules, shared/ copiado como código fuente sin deps propias. Cuando metí R2Store y necesitó @aws-sdk/client-s3 desde shared/, rompió todo: Node ESM resolution buscaba la dep en /app/shared/node_modules que no existía. Workspaces lo arregla automáticamente — la dep se hoistea a la raíz visible para todos.

Un solo Dockerfile parametrizado

El truco que más limpia el repo:

# Dockerfile.workspace — uno solo, sirve a 7 servicios
FROM node:22-alpine
WORKDIR /app
RUN apk add --no-cache ripgrep git

# Layer cacheable: package.json de todos los workspaces
COPY package.json package-lock.json* ./
COPY shared/package.json ./shared/
COPY orchestrator/package.json ./orchestrator/
COPY agents/investigator/package.json ./agents/investigator/
# … resto de workspaces

RUN npm install --include=dev          # hoistea a /app/node_modules

COPY . .                                # source code

ARG SERVICE_PATH                        # parametrizable
WORKDIR /app/${SERVICE_PATH}
CMD ["npm", "start"]

Y en docker-compose.yml cada servicio pasa su SERVICE_PATH:

investigator:
  build:
    context: .
    dockerfile: Dockerfile.workspace
    args:
      SERVICE_PATH: agents/investigator

feature-developer:
  build:
    context: .
    dockerfile: Dockerfile.workspace
    args:
      SERVICE_PATH: agents/feature-developer

Una sola Dockerfile mantenida en lugar de siete. Añadir un nuevo agente (QA Tester mañana, Tech Librarian la semana que viene) es una entrada en docker-compose, cero ficheros nuevos en el directorio del agente más allá del código.

Git: dónde acaba el código que producen los agentes

Hoy:

  • Skills (skills/*.md) → en este repo (agentic-architecture), versionado normalmente
  • Outputs cognitivos (briefs, patches, reports) → Cloudflare R2, bucket agentic-artifacts, key tasks/<task_id>.json
  • Código generado por Feature Developer → cuando el QA Tester esté listo, irá a feat/<slug> en el repo target del cliente vía GitHub API

El repo target es separado del repo de la arquitectura. El sistema agéntico no escribe en su propio código — escribe en el código del proyecto que está ayudando a desarrollar. El Codebase MCP server lo monta read-only:

codebase-mcp:
  volumes:
    - ${TARGET_REPO:-./}:/repo:ro

TARGET_REPO es env var: por defecto el propio repo agentic-architecture (útil para dogfooding y demos), pero apuntar a cualquier ruta en disco lo cambia. Cuando despleguemos a Cloudflare, será un volumen montado desde un clone fresco del repo del cliente.


Los tres MODEs por agente con LLM

Cada agente cognitivo tiene tres modos de operación, controlados por env var:

INVESTIGATOR_MODE=mock     # canned response, $0, instantáneo
INVESTIGATOR_MODE=local    # Ollama + Qwen Coder 32B, $0, ~30s
INVESTIGATOR_MODE=live     # Anthropic Claude Sonnet 4.6, ~$0.01, ~3s

mock para CI y tests de integración. local para desarrollo serio sin gastar API ni filtrar código (privacidad: todo se procesa en tu Mac). live para calidad máxima en producción.

El truco arquitectónico: el if/else está en el agente, no en una clase distinta por modo. Misma firma, distinto motor cognitivo.

if (MODE === "mock") {
  textContent = mockReport(topic);
} else if (MODE === "local") {
  const r = await callLocalLLM({ system, user, maxTokens: 2000 });
  textContent = r.text;
} else {
  const response = await claude.messages.create({ ... });
  textContent = extractText(response);
}

Esa función callLocalLLM vive en shared/local-llm.ts — una sola implementación reutilizada por los 4 agentes con LLM. El día que metas otro modelo local (vLLM, llama.cpp server, otra distro), tocas un archivo, no cuatro.


Cómo se codifica la experiencia humana

Esta sección es la que más me importa, porque es la tesis del proyecto.

1. Nombres → prompts

Los modelos LLM son extraordinariamente sensibles a la identidad declarada. Un agente llamado "Auditor" produce listas de problemas. Uno llamado "Tech Lead" produce specs ejecutables. Mismo input, mismo prompt literal — distinto comportamiento por arrastre del nombre.

Cuando diseñas la red, nombrar bien tus agentes es prompt engineering implícito. "Project Mapper" sugiere mapear; "Codebase Auditor" sugeriría encontrar problemas; "Stack Detector" sugeriría minimalismo. Eligiendo el nombre correcto, sesgas el output del modelo en la dirección que quieres.

2. Skills como markdown intercambiable

Cada agente carga su misión desde un archivo .md que recibe en el payload:

const skillContent = readFileSync(
  resolve(SKILLS_DIR, payload.skill_uri.replace(/^skills\//, "")),
  "utf-8",
);

// Y se lo pasa a Claude como system prompt:
const response = await claude.messages.create({
  system: skillContent,   // ← la "misión"
  messages: [{ role: "user", content: userPrompt }],
});

Los markdown se montan read-only en los containers como volumen Docker:

investigator:
  volumes:
    - ./skills:/app/skills:ro

Editar una skill no requiere rebuild. Cambias el markdown, el siguiente task la usa. Eso convierte la mejora del sistema en algo que un product manager con buen criterio puede hacer sin tocar TypeScript.

3. Constraints en schemas (no en wikis)

El brief del Tech Lead define forbidden_patterns: ["any", "ts-ignore"]. El Feature Developer no puede devolver un patch que contenga esos patterns sin que el schema lo rechace. Es schema-as-policy: la regla de negocio viaja con los datos, no con la documentación.

const DevelopmentResult = z.object({
  patches: z.array(...),
  tests_written: z.number().min(1),       // ≥1, no opcional
  constraints_violated: z.array(z.string())  // si no vacío → state: failed
});

4. follow_pattern: analogía sobre imaginación

El campo más importante del brief no es la lista de archivos — es follow_pattern.reference_file. Apuntar a un archivo real del repo donde ya existe un patrón similar reduce alucinaciones masivamente. El LLM trabaja por analogía con código real, no por imaginación abstracta.

Esto refleja exactamente cómo enseña un senior a un junior: "mira cómo lo hicimos en el módulo de auth, hazlo igual aquí". No le explicamos los patrones desde cero. Apuntamos al ejemplo y pedimos que lo imite.


Ventajas reales del enfoque agéntico

  1. Codifica experiencia que de otro modo se pierde: cuando el senior se va, sus criterios se van con él. Si están en skills/markdowns, no.

  2. Escala paralelizable: 5 features distintas en flight simultáneamente = 5 pipelines independientes. Un equipo humano no escala así sin perder coherencia.

  3. Coste ~$0.10-0.15 por feature (en live mode con Sonnet). Vs. ~150€ de un developer junior por una mañana de trabajo. No estamos reemplazando seniors — estamos paralelizando ejecución.

  4. Resiliencia de proveedor: tres MODEs (mock/local/live) significa que un fallo de Anthropic no bloquea el desarrollo. Caí en esto la semana pasada cuando billing se atascó 6 horas.

  5. Auditabilidad: cada brief, cada patch, cada test report queda persistido en R2 con su task_id. Tres meses después puedes responder "¿por qué este código se diseñó así?" — está literalmente todo guardado.

  6. Velocidad de iteración del sistema mismo: editar una skill markdown y ver cómo cambia el comportamiento del agente en el siguiente task es un feedback loop que no existe en equipos humanos.


Desventajas honestas

  1. Calidad cognitiva por debajo del frontier: Qwen Coder 32B local va muy bien para iterar, pero no llega a Sonnet 4.6 en código complejo. Sonnet 4.6 a su vez no llega a un senior humano experimentado en decisiones arquitectónicas no triviales.

  2. Latencia: pipeline 5-pasos en local mode tarda ~6 minutos. En live ~45 segundos. Compáralo con un senior humano que decide en 30 segundos lo más obvio. No es feedback en tiempo real todavía.

  3. Sandboxing complejo: el Feature Developer hoy no escribe a disco. El QA Tester futuro requerirá Docker-in-Docker o un runner agent con permisos chequeados. Es trabajo serio de infra de seguridad.

  4. Cadena de dependencias: si el Project Mapper falla, todo lo downstream falla. Hay que diseñar fallbacks (mock por agente) y gracefulness (state: failed estructurado), cosa que añade complejidad.

  5. Coste cognitivo de mantener prompts: cuando los modelos LLM cambian (nueva versión, otra familia), las skills pueden necesitar rework.


El riesgo que casi nadie dice: sin senior, no funciona

Aquí va la parte incómoda.

Los agentes IA democratizan la ejecución pero no el criterio. Tener 7 agentes corriendo no convierte a un junior en senior. Convierte a un senior en alguien con superpoderes — y a un junior en alguien produciendo deuda técnica más rápido de lo que puede revisar.

¿Qué pasa cuando un equipo sin experiencia adopta esto?

1. No detectan briefs malos. El Tech Lead puede producir un brief que suene plausible pero apunte a src/features/oauth/ cuando ese path no existe. Un senior lo cazaría leyendo el brief en 10 segundos. Un junior acepta, ejecuta, descubre el problema 2 horas después con el código a medio escribir.

2. No detectan código malo que pasa tests. Los tests miden lo que les pides medir. Si el brief no incluyó el escenario edge case correcto, los tests pasan y el código tiene un bug latente. Un senior huele "esto pasa los tests pero algo huele raro". Un junior celebra el verde y mergea.

3. Confían en el output que parece bueno. Los LLMs producen prosa plausible y código sintácticamente correcto. La diferencia entre "código correcto" y "código plausible" es la línea que separa software que sobrevive 2 años de software que crashea en producción a los 6 meses. Esa línea solo la ve quien ha vivido los crashes anteriores.

4. No saben cuándo parar el loop. El QA Tester va a iterar con el Feature Developer hasta que pasen los tests. Pero si el problema es de diseño (el brief estaba mal), iterar no resuelve — solo introduce parches. Un senior detecta "esto no se arregla con más loops, hay que rediseñar". Un junior lo deja iterar hasta que aparenta funcionar.

5. Producen tech debt a velocidad de IA. Esto es lo más serio. Un junior con un equipo agéntico produce más código que un senior solo. Pero la fracción de ese código que está bien diseñado es menor. El ratio de deuda técnica generada vs. valor entregado se invierte. Y al contrario que un junior humano que aprende de sus errores, el junior con agentes los repite a escala porque no los reconoce.

El rol del senior en un equipo agéntico

No desaparece. Cambia:

  • Diseña las skills: el contenido del markdown es donde reside la experiencia. Editar feature-briefing.md para añadir un constraint nuevo es como impartir formación al Tech Lead.
  • Revisa outputs contra intuición: cuando un brief huele raro, lo detecta. Cuando un patch resuelve los tests pero deja deuda, lo detecta.
  • Caza failure modes: las primeras semanas con el sistema, cualquier caso raro que aparece va a la documentación (futuro Project Tech Librarian) o a un nuevo constraint en las skills.
  • Itera la arquitectura del meta-sistema: ¿hace falta un nuevo agente? ¿hay que separar dos roles que están mezclados? ¿la longitud del brief es óptima? Esas decisiones siguen siendo del senior.

El senior ya no escribe la mayoría del código. Diseña el sistema que escribe el código. Y revisa lo que escribe.

Si tu organización está pensando en adoptar arquitecturas agénticas, la pregunta correcta no es "¿cuántos juniors puedo reemplazar?". Es "¿cuántos seniors necesito para vigilar este sistema?". La respuesta es siempre mayor que cero. Y probablemente más de los que pensabas.


Lo que viene en esta serie

Este post es el manifiesto. La serie técnica que sigue:

  1. Día 1 — Walking skeleton: primer ping A2A funcionando
  2. Día 2 — De Auditor a Tech Lead: por qué los nombres son prompts
  3. Día 3 — A2A + MCP juntos: exponer un repo como Model Context Protocol server
  4. Día 4 — El bloqueo de Anthropic billing y el patrón de tres MODEs con Ollama
  5. Día 5 (próximo) — Comparativa side-by-side Qwen Coder 32B local vs Sonnet 4.6 sobre el mismo brief
  6. Día 6 — Construyendo el QA Tester con loop de iteración
  7. Día 7 — El Project Tech Librarian que hace al sistema aprender
  8. Día N — Despliegue a Cloudflare Workers, sandbox real, multi-tenant

Si te interesa el cómo sin perder de vista el por qué, suscríbete. Cada post mantiene la honestidad de los obstáculos reales — incluyendo los bugs que pisé, los reframes que hice a media implementación, y los momentos donde paré a preguntarme si estaba construyendo algo útil o solo sofisticado.

Este sistema funciona porque está diseñado por alguien que ha visto demasiadas veces lo que pasa cuando no funciona. Si lo replicas, asegúrate de tener la misma carga de cicatrices. Si no las tienes, asegúrate de tener un senior que sí.


Agentikas MVP cierra hoy con: 5 agentes operativos, 1 servidor MCP, 2 agentes pendientes (QA Tester, Tech Librarian), 3 modos por agente (mock/local/live), persistencia en Cloudflare R2, monorepo npm workspaces, y un solo Dockerfile parametrizado para 7 servicios. El código está en el repo. Las cicatrices están en los blog posts que vienen.

Comentarios

Cargando comentarios…