DEV Community

Cover image for Stop Wasting Days on Dev Setup: How We Automated Our Microservices Environment for Every Developer
Mehdi Hassan Jony
Mehdi Hassan Jony

Posted on

Stop Wasting Days on Dev Setup: How We Automated Our Microservices Environment for Every Developer

Stop Wasting Days on Dev Setup: How We Automated Our Microservices Environment for Every Developer

How we reduced new developer onboarding from 3 days to 15 minutes and eliminated configuration drift across 12 microservices.

The Problem That Made Us Snap
Picture this: A new senior developer joins your team. They’re excited, ready to ship code on day one. Then reality hits.
Day 1: They spend 6 hours installing Node, Docker, configuring ESLint to match the team’s style, figuring out which environment variables go where, and asking Slack questions like, “Which version of NestJS are we on again?”
Day 2: They finally get user-service running, but auth-service throws a CORS error because their local config is slightly different from everyone else’s. They spend the afternoon comparing .env files with a teammate.
Day 3: They submit their first PR. The CI pipeline fails because their local Prettier config formats code differently from the shared config that was updated last month — but nobody told them.
Sound familiar?
We had 12 microservices, a team that was growing fast, and a configuration mess that was eating our productivity alive. Every service had slightly different ESLint rules, Docker setups, folder structures, and VS Code settings. Senior developers were spending hours every week answering setup questions instead of building features.
So we built a system that fixed all of it. Here’s exactly how.

The Solution: One Repo to Rule Them All

We created a central configuration repository called dev-environment-config that acts as the single source of truth for every development environment across our entire microservices ecosystem.
The idea is simple:

One repository controls the setup, structure, linting, Docker config, VS Code settings, and shared code for every service. When a senior developer updates it, every developer’s machine automatically syncs the next morning.

The Architecture at a Glance

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   │
└──────────┘ └──────────┘ └──────────┘
Enter fullscreen mode Exit fullscreen mode

Implementation: Step by Step

Step 1: The Central Config Repository

We created a new repository with this structure:

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
Enter fullscreen mode Exit fullscreen mode

The manifest.json is the heartbeat of the system:

{
  "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"
  ]
}
Enter fullscreen mode Exit fullscreen mode

Every time a config changes, we bump the version. This is how the auto-update system knows something changed.

Step 2: The One-Command Onboarding Script

This was the game-changer. We wrote a bash script that a new developer runs once, and their entire environment is ready.

The new developer’s experience:

  1. Install VS Code ✅
  2. Run one command:
curl -sSL https://raw.githubusercontent.com/company/dev-environment-config/main/scripts/setup-new-developer.sh | bash
Enter fullscreen mode Exit fullscreen mode
  1. Wait ~10 minutes. Done.

What the script does under the hood:

#!/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"
Enter fullscreen mode Exit fullscreen mode

That’s it. The developer opens VS Code, and everything is configured — extensions, linting, formatting, Docker, environment variables, the works.

Step 3: The Config Sync Script

This script copies the shared configs from the central repo into every service:

#!/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
Enter fullscreen mode Exit fullscreen mode

Step 4: The Daily Auto-Update (The Magic)

This is the part that keeps everything in sync without anyone thinking about it.
Every morning at 8 AM (weekdays), a cron job runs auto-update.sh on every developer’s machine:

#!/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"
Enter fullscreen mode Exit fullscreen mode

The flow:

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│
    └─────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Step 5: The Shared Common Library

Instead of copying and pasting guards, interceptors, and DTOs across 12 services, we created @company/common-lib — a private NPM package:

// 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,
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

When we update the common library (add a new guard, fix a bug in the exception filter), the auto-update system bumps the version in every service automatically.

Step 6: Service Scaffolding

Creating a new microservice used to take a day of copy-pasting and tweaking. Now it takes 30 seconds:

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
Enter fullscreen mode Exit fullscreen mode

The Benefits (With Real Numbers)
1. Onboarding: 3 Days → 15 Minutes

  • Before: Manual setup following a 40-step Confluence page.
  • After: One curlcommand.
  • Before: 3 days to first productive commit.
  • After: 15 minutes to running all services.
  • Before: "Hey, can you help me with my env vars?" (daily Slack noise).
  • After: Zero setup questions.

2. Configuration Drift: Eliminated
Before, every service slowly diverged. user-service had ESLint v8, auth-service had v7. Prettier configs had subtle differences. Docker setups were copy-pasted and never updated. Now, every service has identical configs, and they stay identical because the sync script overwrites them daily.

3. Senior Developer Time: Saved ~5 Hours/Week
Senior devs were spending roughly an hour a day helping juniors with setup issues, reviewing PRs that failed CI due to formatting, and manually updating configs across services. That time is now spent building features.

4. Consistent Code Quality
Every developer has:

  • The same ESLint rules (auto-fixed on save)
  • The same Prettier config (format on save)
  • The same commit message format (enforced by Husky)
  • The same VS Code extensions (auto-installed)

PR reviews went from "please fix your formatting" to "let’s discuss the architecture."

5. Zero-Effort Config Updates

When a senior developer wants to:

  • 1. Add a new ESLint rule → Push to config repo → Everyone gets it tomorrow
  • 2. Update Docker base image → Push → Auto-synced
  • 3. Add a VS Code extension → Push → Auto-installed
  • 4. Fix a bug in the exception filter → Publish common-lib → Auto-updated No Slack announcements needed. No "please run npm install" messages. It just happens.

6. Standardized Project Structure
Every service looks the same. A developer can jump from user-service to payment-service and immediately know where everything is. The src/common/ folder has the same patterns. The Docker setup is identical. The health check endpoint is at the same path.

What We Learned

Start Small

We didn’t build this all at once. We started with just syncing ESLint and Prettier configs. Once the team saw the value, we added Docker, VS Code settings, the common library, and the auto-update system incrementally.

Don’t Force Everything

We allow service-specific overrides. If payment-service needs a special ESLint rule, they can add a .eslintrc.local.js that extends the shared one. The shared config is the baseline, not a straitjacket.

Notifications Matter
The desktop notification ("✅ Updated to config v1.2.0") was a small touch, but it made developers aware of the system. Before that, updates happened silently and people were confused when things changed.

Version Everything
The manifest.json version tracking was critical. Without it, the auto-update script had no way to know if anything changed, and it would waste time syncing configs that were already up to date.

The Complete Stack

  • Backend: NestJS (TypeScript)
  • Frontend: ReactJS (TypeScript)
  • AI: Qwen MCP
  • Containers: Docker, Docker Compose
  • Runtime: Node.js 20 LTS
  • Database: PostgreSQL 16
  • Cache: Redis 7
  • CI/CD: GitHub Actions
  • Editor: VS Code
  • Config Sync: Bash scripts + Cron

TL;DR
If your team has more than 3 microservices and more than 5 developers, you need a centralized dev environment config. Here’s the recipe:

  1. Create a config repo with templates, shared configs, and scripts.
  2. Write a setup script that onboards new devs in one command.
  3. Write a sync script that copies shared configs to all services.
  4. Set up a cron job that auto-updates every morning.
  5. Create a shared NPM library for reusable code.
  6. Build a service generator for scaffolding new microservices.

The result: developers spend their time writing code, not configuring environments.

Have you solved this problem differently at your company? I’d love to hear about it in the comments.
If you found this useful, give it a clap 👏 and follow for more microservices architecture deep-dives.

Top comments (0)