PlantUML diagrams evolve with git: renaming, migration, archival

puml.online

Writing the .puml isn’t the end. Three months later: services get renamed, architectures refactored, components deprecated — the diagram has to evolve alongside git to avoid the awkward “docs say A, code has been calling it B for months” moment. This is the rename migration toolkit, archival strategy, and git log sync.

Two layers of renaming

Layer 1: filename renameuser-service.pumlidentity-service.puml (service renamed to identity)

Layer 2: in-diagram reference renamecomponent "user-service"component "identity-service", and every --> us becomes --> identity

Both must be in sync — rename the file but not the references inside, git diff won’t show it; rename references but not the file, git history breaks.

Tool 1: git mv combined with sed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. rename file
git mv docs/diagrams/user-service.puml docs/diagrams/identity-service.puml

# 2. grep references across all diagrams
grep -rl "user-service\|user_service\|us-" docs/diagrams/*.puml

# 3. replace
find docs/diagrams -name "*.puml" -exec sed -i 's/user-service/identity-service/g; s/user_service/identity_service/g; s/us-/identity-/g' {} +

# 4. commit
git add -A
git commit -m "refactor(diagrams): rename user-service to identity-service

- git mv user-service.puml -> identity-service.puml
- sed replace all 'user-service' / 'us-' across diagrams
- verified with hexo generate"

git mv beats manual mv + add because git detects the rename — even if you modify content after renaming, git’s rename detection still catches it.

Tool 2: bash script for renaming with dry-run, error handling, grep verification

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash
# rename-component.sh
set -euo pipefail

OLD="$1"
NEW="$2"

# 1. rename file (assuming filename contains component name)
if ls docs/diagrams/*"${OLD}"*.puml >/dev/null 2>&1; then
git mv docs/diagrams/*"${OLD}"*.puml docs/diagrams/*"${NEW}"*.puml 2>/dev/null || true
fi

# 2. replace in diagram text
find docs/diagrams -name "*.puml" -exec sed -i \
"s/component \"${OLD}\"/component \"${NEW}\"/g; \
s/as ${OLD}/as ${NEW##*_}/g" \
{} +

# 3. verify
if grep -q "${OLD}" docs/diagrams/*.puml; then
echo "WARN: leftover '${OLD}' references found:"
grep -l "${OLD}" docs/diagrams/*.puml
exit 1
fi

echo "Renamed '${OLD}' → '${NEW}' successfully"
1
2
chmod +x rename-component.sh
./rename-component.sh user-service identity-service

Tool 3: PlantUML !define for indirect reference

Prevention is better than cure — start with !define to abstract the name:

1
2
3
4
5
!define USER_SVC identity-service

component USER_SVC as us
order-service --> USER_SVC
@enduml

Renaming a service means changing the !define line once — but this convention only works if the team honors it, otherwise newcomers write identity-service directly, bypassing the define.

Tool 4: CI detection of “code vs diagram” drift

Architecture diagrams’ most common rot — code renamed a service, diagram didn’t.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# tests/test_architecture_drift.py
import re, pathlib, yaml

PUML_TEXT = pathlib.Path("docs/diagrams/architecture.puml").read_text()

# read services from code (assume one directory per service)
services_in_code = {
p.name for p in pathlib.Path("services").iterdir() if p.is_dir()
}

# read component names from diagram
services_in_diagram = set(re.findall(r'component\s+"([^"]+)"', PUML_TEXT))

# detect
missing_in_diagram = services_in_code - services_in_diagram
stale_in_diagram = services_in_diagram - services_in_code

assert not missing_in_diagram, f"Code has services not in diagram: {missing_in_diagram}"
assert not stale_in_diagram, f"Diagram has services not in code: {stale_in_diagram}"

CI runs the test — code changes a service but diagram doesn’t → fail.

Architecture split: diagram evolution

Service split from monolith to microservices, the diagram follows.

Phase 1: monolith

1
2
3
4
5
6
7
8
@startuml
package "Monolith" {
[User]
[Order]
[Payment]
[Inventory]
}
@enduml

Phase 2: extract User service

1
2
3
4
5
6
7
8
9
10
11
12
@startuml
package "User Service" {
[User]
}
package "Monolith" {
[Order]
[Payment]
[Inventory]
}

User --> Monolith : HTTP
@enduml

Old and new diagrams coexistarchitecture-v1-monolith.puml (archived) + architecture.puml (current).

Phase 3: extract Order

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@startuml
package "User Service" as user_svc {
[User]
}
package "Order Service" as order_svc {
[Order]
}
package "Monolith" {
[Payment]
[Inventory]
}

user_svc --> order_svc : HTTP
order_svc --> Monolith : HTTP
@enduml

The evolution stays traceable in gitnew hires can read git log to see architecture history.

Archive strategy

Old diagrams shouldn’t be deleted directly — put them in archive/ with date prefix:

1
2
3
4
5
6
7
docs/
├── diagrams/
│ ├── architecture.puml # current
│ ├── sequence-login.puml # current
│ └── archive/
│ ├── 2025-03-monolith.puml # March architecture
│ └── 2025-09-monolith-with-user.puml # September split

_config.yml configures skip_render to prevent archive rendering:

1
2
skip_render:
- 'diagrams/archive/**'

The archive directory doesn’t enter the CI flow, but git history has it.

Cross-version reference: use git history as diagram metadata

PlantUML doesn’t support in-diagram git references — but you can read git log via %load_json:

1
2
3
# generate diagrams/git-history.json
git log --format='{"date":"%ai","message":"%s","author":"%an"}' \
docs/diagrams/architecture.puml > docs/diagrams/git-history.json
1
2
3
4
5
6
7
8
9
10
@startuml
!include docs/diagrams/git-history.json

note as N1
Recent commits:
!foreach $commit in %json()
* $commit.date: $commit.message
!endforeach
end note
@enduml

Diagram shows recent commits to itself — who changed it, when, what.

Doc-code sync via git hooks

Client-side hook auto-syncs the diagram at commit time:

1
2
3
4
5
6
7
8
9
10
# .git/hooks/pre-commit
#!/bin/bash

CHANGED_FILES=$(git diff --cached --name-only -- 'services/**/*.go')

if [ -n "$CHANGED_FILES" ]; then
echo "Detected service code changes — auto-regenerating architecture diagram..."
python tools/gen_architecture_diagram.py
git add docs/diagrams/architecture.puml
fi

When committing service code, the architecture diagram auto-regenerates and joins the commit — developers don’t manually edit the diagram.

Year-spanning refactor: diagram migration

Scenario: massive service renames (e.g. user-service → identity-service plus 30 other services renamed in one go):

Steps:

  1. Freeze diagram edits — README says “service rename in progress, diagram updates paused”
  2. Batch change code + diagram — single PR does it all, don’t split into multiple
  3. CI verify — drift test must pass before merge
  4. Delete old + archive — after rename completes, old user-service.puml moves to archive/
  5. Update docs — README references new service names

Conversely — if the diagram is already stale (untouched for 3 months):

  1. Acknowledge staleness — README top adds ⚠️ “diagram may differ from code; code wins”
  2. File a redraw ticket — dedicated ticket to redraw
  3. Add drift test — prevent the next round of rot

Field foot-guns

  • git rename detection doesn’t fire — you change >50% of file content, git treats it as delete + add, rename not shown. Rename + replace in same commit — only then rename detection works.
  • PlantUML alias us doesn’t track user-service → identity rename — alias is syntactic sugar, renaming or not doesn’t affect rendering, but grep for us will hit us-east-1 and similar. Use grep with anchored regex: grep -E "(as|component) +us\b".
  • Cross-language references — Java service called UserService, Go user-service, PlantUML uses user-servicestandardize on kebab-case is the simplest convention.
  • CI test flakes — drift test requires service directories to strictly match the diagram, but sometimes the diagram intentionally has “future services” — mark with // future: comment:
    1
    // future: payment-service (not yet deployed)
    Test skips components marked with future: comment.

Summary

Scenario Tool
Service rename git mv + sed
Architecture split archive old + new in phases
Prevent rot CI drift test
Auto-sync git pre-commit hook calling Python
Year-spanning refactor single PR for everything, no intermediate state

Core principle: diagram and code evolve together — either auto-generated (with drift test as safety net) or manually edited with CI reminder. No automation means the diagram doesn’t survive long-term.

  • Title: PlantUML diagrams evolve with git: renaming, migration, archival
  • Author: puml.online
  • Created at : 2026-07-30 17:15:00
  • Updated at : 2026-08-14 21:34:29
  • Link: https://puml.online/blog/plantuml-versioning-renaming-en/
  • License: This work is licensed under CC BY-NC-SA 4.0.