Un repositorio de configuración central para microservicios: integración y sincronización con un solo comando
Cree un repositorio central de configuración con scripts de configuración, sincronización y actualización diaria para que todos los servicios de NestJS y React compartan las configuraciones de linting, editor y Docker.
Cuando un equipo ejecuta una docena de microservicios Node.js, la parte más difícil de la primera semana de un nuevo empleado suele ser no el código sino el entorno: la versión correcta de Node, reglas compatibles con ESLint y Prettier, los valores adecuados del .env y una configuración de Docker que funcione como la de los demás. Cada servicio tiende a desviarse un poco, y los ingenieros senior terminan actuando como servicio de soporte. Este artículo muestra cómo solucionar esto con un único repositorio de configuración además de algunos scripts en Bash que inicializan una máquina con un solo comando, copian los ajustes compartidos a cada servicio y los mantienen actualizados. También verá en qué aspectos los scripts, tal como están escritos, necesitan mejoras antes de poder confiar en ellos.
Cómo se introduce la desviación en la configuración
Imagínese a un desarrollador experimentado que se une a un equipo responsable de doce servicios. El primer día se pasa instalando Node y Docker, definiendo el estilo ESLint del equipo, intentando adivinar qué variables pertenecen a cada archivo .env y preguntando por chat qué versión de NestJS utilizan los servicios. Luego, auth-service falla debido a un error CORS causado por una configuración local ligeramente diferente, y más tarde la primera solicitud de pull request falla en las pruebas automatizadas porque la configuración compartida de Prettier cambió hace un mes y la copia local nunca se actualizó.
Ninguno de estos fallos es grave, pero juntos resultan costosos. Cuando cada servicio tiene sus propias reglas de lint ligeramente distintas, archivos Dockerfile, estructura de carpetas y configuraciones del editor, el costo se manifiesta en días perdidos para los recién llegados y horas semanales desperdiciadas por los ingenieros senior respondiendo preguntas sobre la configuración.
Un repositorio como fuente de verdad
La solución es un repositorio dedicado, denominado aquí dev-environment-config, que contiene todo lo necesario para una máquina de desarrollo: plantillas de proyecto, reglas de lint y formato, configuraciones de VS Code, configuración básica de Docker, scripts de automatización y una biblioteca compartida. Cuando alguien lo actualiza, cada máquina de desarrollador recibe automáticamente los cambios a la mañana siguiente.
La estructura general es la siguiente, con el repositorio central que suministra datos a cada servicio:
dev-environment-config (Central Repo)
├── templates/ → Project scaffolding (NestJS, React)
├── shared/ → ESLint, Prettier, Git configs
├── vscode/ → Extensions & settings
├── docker/ → Base Docker configs
├── scripts/ → Automation (setup, sync, update)
├── common-lib/ → Shared NPM package
└── manifest.json → Version tracking
│ syncs to
▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ user-svc │ │ auth-svc │ │ pay-svc │ ... 12 services
│ .eslint │ │ .eslint │ │ .eslint │
│ .vscode │ │ .vscode │ │ .vscode │
│ Docker │ │ Docker │ │ Docker │
└──────────┘ └──────────┘ └──────────┘
Construyendo el sistema paso a paso
Estructura del repositorio central
Dentro del repositorio, cada área tiene su propia carpeta. Las plantillas albergan iniciadores completos para NestJS y React; shared/ contiene archivos de configuración para ESLint, Prettier, EditorConfig, reglas de ignorado en Git y commitlint; vscode/ guarda las configuraciones del editor, y scripts/ alberga los scripts de automatización.
dev-environment-config/
├── templates/
│ ├── nestjs-service/ # Complete NestJS starter
│ └── reactjs-service/ # Complete React starter
├── shared/
│ ├── .eslintrc.js
│ ├── .prettierrc
│ ├── .editorconfig
│ ├── .gitignore
│ └── commitlint.config.js
├── vscode/
│ ├── extensions.json
│ ├── settings.json
│ └── launch.json
├── scripts/
│ ├── setup-new-developer.sh
│ ├── sync-config.sh
│ ├── auto-update.sh
│ └── create-new-service.sh
├── common-lib/ # Shared NPM package
└── manifest.json # Version tracker
El archivo manifest.json es lo que hace posible el resto de la automatización. Registra la versión general, cuándo y por quién se modificó por última vez, las versiones para cada área de configuración y la lista de servicios que pertenecen al espacio de trabajo.
{
"version": "1.2.0",
"lastUpdated": "2024-01-15T10:00:00Z",
"updatedBy": "senior-dev",
"configs": {
"eslint": "3.2.0",
"prettier": "2.1.0",
"docker": "1.5.0"
},
"services": [
"user-service",
"auth-service",
"payment-service"
]
}
La regla es sencilla: cualquier cambio en la configuración compartida aumenta la versión de nivel superior. El script de actualización compara posteriormente las versiones para decidir si hay algo que sincronizar; por lo tanto, olvidarse de aumentarla significa que el cambio nunca se propagará silenciosamente. Una verificación en CI que falla cuando los archivos bajo shared/ cambian sin un aumento de versión constituye una medida de seguridad económica.
Onboarding con un solo comando
La mayor ventaja es el script de arranque. Un nuevo desarrollador instala VS Code y luego ejecuta un único comando:
curl -sSL https://raw.githubusercontent.com/company/dev-environment-config/main/scripts/setup-new-developer.sh | bash
Aproximadamente diez minutos después, el espacio de trabajo está listo. Es conveniente ejecutar directamente un script remoto desde bash, pero se debe actuar con precaución: el script se ejecuta con los permisos del usuario y con lo que haya en la rama en ese momento. Para una herramienta interna esto suele ser aceptable, sin embargo, muchos equipos prefieren que las personas clonen el repositorio y ejecuten el script localmente para poder leerlo primero.
En realidad, el script funciona siguiendo una secuencia fija:
#!/bin/bash
set -e
WORKSPACE_DIR="$HOME/company-workspace"
CONFIG_REPO="https://github.com/company/dev-environment-config.git"
echo "🚀 Setting up your development environment..."
# 1. Check & install prerequisites
check_and_install "git" "Git"
check_and_install "node" "Node.js" "nvm install 20"
check_and_install "docker" "Docker"
# 2. Create workspace & clone everything
mkdir -p "$WORKSPACE_DIR" && cd "$WORKSPACE_DIR"
git clone "$CONFIG_REPO"
# 3. Clone all service repos from manifest
SERVICES=$(cat dev-environment-config/manifest.json | \
python3 -c "import sys,json; \
[print(s) for s in json.load(sys.stdin)['services']]")
for service in $SERVICES; do
git clone "https://github.com/company/${service}.git"
done
# 4. Install VS Code extensions
EXTENSIONS=$(cat dev-environment-config/vscode/extensions.json | \
python3 -c "import sys,json; \
[print(e) for e in json.load(sys.stdin)['recommendations']]")
for ext in $EXTENSIONS; do
code --install-extension "$ext" --force
done
# 5. Sync shared configs to all services
bash dev-environment-config/scripts/sync-config.sh
# 6. Install dependencies everywhere
for service in $SERVICES; do
cd "$service" && npm install && cp .env.example .env && cd ..
done
# 7. Set up daily auto-update cron (8 AM weekdays)
CRON="0 8 * * 1-5 cd $WORKSPACE_DIR && \
bash dev-environment-config/scripts/auto-update.sh"
(crontab -l 2>/dev/null; echo "$CRON") | crontab -
echo "✅ Setup complete! Open VS Code: code $WORKSPACE_DIR"
Hay algunos detalles que merecen atención. set -e interrumpe la ejecución en el primer comando que falla, lo cual evita que un espacio de trabajo parcialmente configurado parezca completo. La función auxiliar check_and_install no se muestra y debe escribirse según el sistema operativo. El análisis de JSON mediante una línea de comandos en Python funciona, pero el script de actualización utiliza posteriormente jq; por lo tanto, estandarizar en una sola herramienta reduce los requisitos previos. Copiar .env.example proporciona valores por defecto seguros, pero solo funciona si los archivos de ejemplo se mantienen completos y no contienen secretos reales.
Sincronización de la configuración compartida en cada servicio
El script de sincronización recorre los servicios y copia los archivos dotfiles compartidos así como las configuraciones de VS Code en cada uno, sobrescribiendo lo que ya existe allí.
#!/bin/bash
# sync-config.sh
CONFIG_ROOT="$(dirname "$SCRIPT_DIR")"
WORKSPACE_DIR="$(dirname "$CONFIG_ROOT")"
for service in $SERVICES; do
SERVICE_DIR="$WORKSPACE_DIR/$service"
# Copy shared configs
cp -f "$CONFIG_ROOT/shared/.eslintrc.js" "$SERVICE_DIR/"
cp -f "$CONFIG_ROOT/shared/.prettierrc" "$SERVICE_DIR/"
cp -f "$CONFIG_ROOT/shared/.editorconfig" "$SERVICE_DIR/"
cp -f "$CONFIG_ROOT/shared/.gitignore" "$SERVICE_DIR/"
# Copy VS Code settings
mkdir -p "$SERVICE_DIR/.vscode"
cp -f "$CONFIG_ROOT/vscode/settings.json" "$SERVICE_DIR/.vscode/"
cp -f "$CONFIG_ROOT/vscode/extensions.json" "$SERVICE_DIR/.vscode/"
echo "✅ $service synced"
done
El fragmento asume que SCRIPT_DIR y SERVICES ya están definidos, por lo que el script real debe obtenerlos por sí mismo (por ejemplo, a partir de $0 y el manifiesto). Las llamadas a cp -f son intencionales: la sobrescritura es el método para eliminar las desviaciones. La contrapartida es que cualquier edición local en esos archivos se perderá en la siguiente ejecución, por lo que la personalización por servicio debe realizarse en archivos separados, como se explicará a continuación. También considere si un archivo como .gitignore realmente debería ser idéntico en todas partes, ya que los servicios a menudo necesitan sus propias entradas.
La tarea de actualización automática diaria
Esta es la parte que mantiene las máquinas alineadas sin que nadie tenga que acordarse de hacer nada. Cada día laborable a las 8 AM, la tarea cron ejecuta auto-update.sh:
#!/bin/bash
# auto-update.sh — Runs daily at 8 AM
cd "$CONFIG_ROOT"
# 1. Check for config updates
OLD_VERSION=$(cat manifest.json | jq -r '.version')
git pull origin main
NEW_VERSION=$(cat manifest.json | jq -r '.version')
if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
echo "🆕 Config updated: $OLD_VERSION → $NEW_VERSION"
bash "$SCRIPT_DIR/sync-config.sh"
fi
# 2. Pull latest code for all services
for service in $SERVICES; do
cd "$WORKSPACE_DIR/$service"
# Stash local changes safely
git stash push -m "auto-update-$(date +%Y%m%d)" 2>/dev/null
# Pull latest
git pull origin $(git branch --show-current) --rebase
# Update dependencies if package.json changed
if git diff HEAD~1 --name-only | grep -q "package.json"; then
npm install
fi
# Update shared library
npm update @company/common-lib
done
# 3. Notify the developer
notify-send "Dev Environment" "✅ Updated to config v$NEW_VERSION"
Trate este script como un punto de partida y no como algo que se debe desplegar sin cambios:
- Los cambios se guardan temporalmente pero nunca se aplican, por lo que un desarrollador que dejó su trabajo sin guardar durante la noche debe saber cómo ejecutar
git stash pop; de lo contrario, el trabajo parece desaparecer. git diff HEAD~1solo examina el último commit. Si la operación de pull trajo varios commits, se pasarán por alto los cambios enpackage.jsonde uno anterior; comparar con el commit que había antes de hacer el pull es más fiable.- Un rebase que encuentra conflictos deja el repositorio en medio del proceso sin que nadie lo supervise. Ejecutar
git pull --ff-onlyy omitir el servicio en caso de error es más seguro para tareas sin supervisión.
notify-send solo está disponible en escritorios Linux, y cron no se ejecuta en una máquina que está en modo suspensión a las 8 de la mañana, por lo que los usuarios de macOS pueden necesitar launchd o un disparador diferente.El flujo de extremo a extremo conecta el envío de un ingeniero senior con la acción del siguiente día por la mañana de cada desarrollador:
Senior Dev pushes config change
│
▼
GitHub Actions
│
┌────┴────┐
│ Slack │ Notify team
│ Publish │ common-lib to NPM
└────┬────┘
│
Next 8 AM
│
▼
Every developer's cron runs
│
┌────┴────────────────┐
│ Pull config repo │
│ Sync shared configs │
│ Pull service repos │
│ Update dependencies │
│ Desktop notification│
└─────────────────────┘
Una biblioteca compartida en lugar de copiar y pegar
Las protecciones, interceptores, filtros de excepciones y DTOs tienden a duplicarse entre los servicios y luego a divergir. Al empaquetarlos como un módulo npm privado, @company/common-lib, cada servicio dispone de una única importación:
// In any service, just import and use:
import {
AuthGuard,
RolesGuard,
ResponseInterceptor,
LoggingInterceptor,
HttpExceptionFilter,
PaginationDto,
McpService, // Qwen MCP integration
} from '@company/common-lib';
@Controller('users')
@UseGuards(AuthGuard, RolesGuard)
@UseInterceptors(ResponseInterceptor)
@UseFilters(HttpExceptionFilter)
export class UserController {
constructor(private mcpService: McpService) {}
@Get()
findAll(@Query() pagination: PaginationDto) {
return this.userService.findAll(pagination);
}
@Post('analyze')
async analyze(@Body() dto: AnalyzeDto) {
return this.mcpService.callTool('text-analysis', {
text: dto.text,
});
}
}
Un controlador puede aplicar las protecciones de autenticación y roles compartidas, el interceptor de respuestas y el filtro de excepciones HTTP, aceptar un DTO común para paginación y utilizar un wrapper McpService para la integración con Qwen MCP. Cuando la biblioteca recibe una nueva protección o una corrección para el filtro de excepciones, la tarea de actualización automática la integra en todos los servicios.
Tenga en cuenta que npm update respeta el rango semver de cada package.json, por lo que una versión mayor no se actualizará automáticamente, lo cual suele ser lo deseado. Las bibliotecas compartidas también generan acoplamiento entre servicios; el artículo en cuándo debería existir código compartido explica cómo decidir qué contenido pertenece a cada una.
Creación de nuevos servicios
Antes, crear un servicio nuevo significaba pasar todo un día copiando uno existente y eliminando lo que no era aplicable. Un script generador convierte ese proceso en una breve solicitud interactiva:
bash scripts/create-new-service.sh
# Prompts:
# Service name: order-service
# Description: Handles order processing
# Port: 3002
# Type: nestjs
# ✅ Done! Complete service with:
# - src/common/ (filters, guards, interceptors, DTOs)
# - Docker setup (Dockerfile + docker-compose)
# - VS Code settings
# - ESLint + Prettier
# - Health check endpoint
# - Swagger docs
# - MCP integration
# - Auto-update scripts
Dado que cada nuevo servicio parte del mismo template, comienza sin desviaciones.
Beneficios para el equipo
Un equipo que adoptó esta configuración reportó los siguientes efectos:
- Onboarding en minutos: un único comando reemplazó una página de wiki con cuarenta pasos, reduciendo el tiempo necesario para ejecutar cada servicio de aproximadamente tres días a unos quince minutos, y las preguntas sobre la configuración en el chat prácticamente desaparecieron.
- Sin más desviaciones: los servicios solían divergir silenciosamente (ESLint 8 en uno, 7 en otro, Dockerfiles obsoletos); las sobrescripciones programadas mantienen su identidad.
src/common/, configuración de Docker y ruta de verificación de estado en cada servicio.Lecciones para su implementación gradual
Comience con la parte más pequeña y útil
No es necesario crear todo esto de una sola vez. Sincronizar solo la configuración de ESLint y Prettier aporta beneficios visibles rápidamente. La configuración de Docker, los ajustes del editor, la biblioteca compartida y las actualizaciones programadas pueden implementarse una vez que el equipo confíe en este enfoque.
Deje espacio para sobrescrituras locales
Una línea base compartida no debe ser una restricción estricta. Un servicio que necesite una regla adicional puede crear un archivo .eslintrc.local.js que extienda la configuración compartida; como el script de sincronización nunca modifica ese archivo, la sobrescritura se mantiene en cada ejecución.
Haga que las actualizaciones sean visibles
La notificación en el escritorio parece un detalle menor, pero informa a los desarrolladores de que el sistema existe y de que algo ha cambiado. Las actualizaciones silenciosas suelen confundir a las personas cuando sus herramientas comienzan a comportarse de manera diferente de la noche a la mañana.
Versione todo
Sin la versión del manifiesto, el proceso de actualización no puede determinar si ha habido cambios y tendría que sincronizarse en cada ejecución. La versionado también proporciona un punto de referencia concreto cuando un cambio causa problemas.
La tecnología detrás del ejemplo
La configuración del ejemplo combinó NestJS y React (ambos en TypeScript), Qwen al que se accedía a través de MCP, Docker y Docker Compose, Node.js 20 LTS, PostgreSQL 16, Redis 7, GitHub Actions, VS Code y Bash, además de cron para la sincronización. Esas versiones reflejan la configuración en ese momento; verifica las versiones LTS actuales antes de copiarlas.
Puntos clave
- Cuando se cuenta con más de unos pocos servicios y desarrolladores, un repositorio central de configuración rinde rápidamente sus frutos.
Lecturas relacionadas
- Comprobación vs. Ejecución: Dónde encaja el compilador Go de TypeScript 7 en su herramientaria — Aprenda qué acelera realmente el compilador Go nativo de TypeScript 7, por qué herramientas como Angular y typescript-eslint van a la zaga, y cómo separar la comprobación de tipos de la ejecución.
- Construyendo un agente AI personal en el que se puede confiar: Memoria, herramientas y acceso ganado — Cómo un agente personal como Hermes Agent combina memoria, herramientas y enrutamiento de modelos, y cómo desplegarlo de forma segura con roles restringidos, fuentes de bajo riesgo y acceso ganado.