18 KiB
Troubleshooting Guide - C2 Container Analysis
This document provides solutions to common problems encountered during C4 Model Level 2 (Container) analysis.
Problem: Too Many Containers Identified
Symptom: 15+ containers for a simple web application
Root Cause: Confusing C3 components with C2 containers
Solution: Remember the deployment boundary:
- Containers are deployable units (can be deployed independently)
- Components are code modules within containers (cannot be deployed alone)
- If it can't be deployed independently, it's probably C3
Decision Test:
Can this be deployed separately?
├─ ✅ Yes → C2 Container
└─ ❌ No → C3 Component
Examples:
| Item | Level | Reason |
|---|---|---|
| React SPA (builds to static files) | C2 Container | Deployed to CDN/web server |
| Auth module within React app | C3 Component | Part of SPA bundle |
| Express API server | C2 Container | Runs as separate process |
| Payment controller in Express | C3 Component | Code within API server |
| PostgreSQL database | C2 Container | Runs as separate process |
| User table in database | C3 Component | Schema within database |
HARD LIMITS (Mandatory Validation):
| Count | Classification | Action Required |
|---|---|---|
| 3-10 | ✅ Standard | Correct C2 abstraction level |
| 11-15 | ⚠️ Review Required | Each container MUST have explicit deployment boundary justification |
| 16-20 | ⚠️ Microservices | Document why this is microservices architecture, not over-granular |
| 20+ | 🛑 STOP | You are likely at C3 level. Mandatory review of every container. |
RULE for 20+ Containers:
- STOP analysis immediately
- Review EVERY identified "container" against the deployment boundary test
- Each item must answer YES to: "Can this be deployed independently?"
- If most answers are NO, you are at C3 level - consolidate to actual containers
- This is NOT guidance - this is a mandatory validation checkpoint
Problem: Can't Determine Technology Stack
Symptom: No clear framework or language indicators
Solution Steps:
Step 1: Check All Package Manifests
# Find all package files
find . -name "package.json" -o -name "requirements.txt" -o -name "pom.xml" -o -name "Cargo.toml"
# Examine dependencies
cat package.json | jq '.dependencies'
cat requirements.txt
Step 2: Check Dockerfiles
# Find Dockerfiles
find . -name "Dockerfile*"
# Check base image
cat Dockerfile | grep "FROM"
Common base images:
FROM node:18→ Node.js 18FROM python:3.11→ Python 3.11FROM openjdk:17→ Java 17FROM nginx:alpine→ Nginx web server
Step 3: Count File Types
# Count source files by extension
find src -name "*.ts" | wc -l # TypeScript
find src -name "*.js" | wc -l # JavaScript
find src -name "*.py" | wc -l # Python
find src -name "*.java" | wc -l # Java
find src -name "*.go" | wc -l # Go
find src -name "*.rs" | wc -l # Rust
Step 4: Check Build Configurations
# Check for framework config files
ls -la webpack.config.js tsconfig.json angular.json vue.config.js next.config.js
# Read framework configs
cat tsconfig.json | jq '.compilerOptions.jsx' # React indicator
cat angular.json | jq '.projects' # Angular indicator
Step 5: Search for Framework Imports
# JavaScript/TypeScript frameworks
grep -r "from 'react'\|from 'vue'\|from '@angular" src/ | head -5
# Python frameworks
grep -r "from django\|from flask\|from fastapi" . | head -5
# Java frameworks
grep -r "import.*springframework" src/ | head -5
Step 6: If Still Unclear - RESTRICTED USE ONLY
"Unknown" is ONLY valid when ALL of these conditions are true:
- ❌ NO manifest files exist (no package.json, requirements.txt, pom.xml, Cargo.toml, go.mod, Gemfile)
- ❌ NO Dockerfile exists OR Dockerfile does not specify version in FROM statement
- ❌ NO build configuration files found (tsconfig.json, angular.json, etc.)
- ❌ NO import statements reveal framework
If ANY manifest file exists, you MUST extract the version. "Unknown" is NOT acceptable.
When "Unknown" is legitimately required:
- Mark technology as "Unknown" temporarily
- Document why it's unclear with evidence of what you searched:
{ "id": "obs-tech-unknown", "category": "technology", "severity": "critical", "description": "Unable to determine primary framework. Searched for all standard manifest files - none found.", "evidence": { "type": "command", "location": "find . -name 'package.json' -o -name 'requirements.txt' -o -name 'pom.xml' -o -name 'Cargo.toml' -o -name 'go.mod'", "snippet": "No results found" } } - Ask user for clarification immediately
- This triggers a CRITICAL observation, not a warning
RULE: If you mark something as "Unknown" when manifest files exist, this is a VIOLATION of C2 methodology.
Problem: Container vs System Confusion
Symptom: Unclear if something is C1 System or C2 Container
Remember the Levels:
| Level | Abstraction | Example |
|---|---|---|
| C1 System | High-level logical system with business purpose | "E-Commerce System" |
| C2 Container | Deployable/runnable unit within a system | "React SPA", "Express API", "PostgreSQL DB" |
| C3 Component | Code module within a container | "ProductList.tsx", "PaymentController.ts" |
Decision Tree:
Does it have a business purpose understood by non-technical stakeholders?
├─ ✅ Yes → Could be C1 System
│ └─ Does it consist of multiple deployable units?
│ ├─ ✅ Yes → C1 System
│ └─ ❌ No → C2 Container (simple system with one container)
│
└─ ❌ No → Not C1 System
└─ Can it be deployed independently?
├─ ✅ Yes → C2 Container
└─ ❌ No → C3 Component
Real Examples:
Example 1: E-Commerce System
- "E-Commerce System" → C1 System (business purpose: sell products online)
- "Customer Web Portal" → C2 Container (React SPA)
- "Product API Server" → C2 Container (Express API)
- "Order Database" → C2 Container (PostgreSQL)
- "Product Catalog Component" → C3 Component (code in React SPA)
Example 2: Payment Processing System
- "Payment Processing System" → C1 System (business purpose: process payments)
- "Payment API Server" → C2 Container (NestJS API)
- "Payment Database" → C2 Container (PostgreSQL)
- "Stripe Integration Service" → C2 Container (Python worker)
- "PaymentController" → C3 Component (code in NestJS API)
Example 3: Monolith Application
- "Customer Portal System" → C1 System
- "Next.js Full-Stack Application" → C2 Container (single deployable unit)
- Frontend pages → C3 Components
- API routes → C3 Components
- "PostgreSQL Database" → C2 Container
- "Next.js Full-Stack Application" → C2 Container (single deployable unit)
Problem: Missing Required Fields in Validation
Symptom: Validation script fails with "Missing required field"
Common Missing Fields:
1. system_id
Error: Missing required field: system_id
Solution:
# Check c1-systems.json for valid system IDs
cat c1-systems.json | jq '.systems[].id'
# Use exact ID (case-sensitive)
# Example: "ecommerce-system" NOT "Ecommerce-System"
2. technology.primary_language
Error: Missing required field: technology.primary_language
Solution:
- JavaScript, TypeScript, Python, Java, Go, Rust, etc.
- Required for all containers except pure infrastructure
- Use capitalized language name
Example:
{
"technology": {
"primary_language": "TypeScript",
"framework": "React 18.2.0"
}
}
3. technology.framework
Error: Missing required field: technology.framework
Solution:
- React, Express, Django, Spring Boot, FastAPI, etc.
- Required for all application containers
- Include version number
Example:
{
"technology": {
"primary_language": "JavaScript",
"framework": "Express.js 4.18.2"
}
}
Exception: Infrastructure containers (databases, caches) may use "N/A":
{
"technology": {
"primary_language": "N/A",
"framework": "PostgreSQL 15.3"
}
}
4. runtime.environment
Error: Missing required field: runtime.environment
Valid Values:
browser- Client-side web applicationserver- Server-side applicationmobile- iOS/Android applicationcloud- Cloud-native/serverlessedge- Edge computingdesktop- Desktop application
Example:
{
"runtime": {
"environment": "server",
"platform": "Linux x64, Node.js 18.16.0"
}
}
5. runtime.platform
Error: Missing required field: runtime.platform
Solution: Describe the exact platform where the container runs.
Examples:
// Browser
"platform": "Modern browsers (Chrome 90+, Firefox 88+, Safari 14+)"
// Server
"platform": "Linux x64, Node.js 18.16.0"
"platform": "Linux x64, Python 3.11.4"
"platform": "Linux x64, JVM 17"
// Mobile
"platform": "iOS 14+, Android 11+"
// Cloud
"platform": "AWS Lambda (Node.js 18 runtime)"
6. runtime.containerized
Error: Missing required field: runtime.containerized
Solution:
true- Runs in Docker/Kubernetes/containerfalse- Runs directly on OS or in browser
Examples:
// Containerized API
{
"runtime": {
"containerized": true,
"container_technology": "Docker",
"deployment_model": "Kubernetes with 3 replicas"
}
}
// Browser SPA
{
"runtime": {
"containerized": false
}
}
Problem: Timestamp Validation Fails
Symptom: "Timestamp must be newer than parent timestamp"
Root Cause: c2-containers.json timestamp must be > c1-systems.json timestamp
Solution:
Step 1: Check Current Timestamps
# Check C1 timestamp
cat c1-systems.json | jq '.metadata.timestamp'
# Example output: "2025-11-17T10:00:00.000Z"
# Check C2 timestamp
cat c2-containers.json | jq '.metadata.timestamp'
# Example output: "2025-11-17T09:00:00.000Z" # WRONG! Earlier than C1
Step 2: Regenerate with Current Timestamp
Ensure ISO 8601 format: YYYY-MM-DDTHH:mm:ss.sssZ
# Generate current timestamp
date -u +"%Y-%m-%dT%H:%M:%S.000Z"
# Example: 2025-11-17T20:20:00.000Z
Step 3: Update c2-containers.json
{
"metadata": {
"timestamp": "2025-11-17T20:20:00.000Z",
"generated_by": "c2-abstractor",
"parent": {
"file": "c1-systems.json",
"timestamp": "2025-11-17T10:00:00.000Z"
}
}
}
Rule: Always ensure C2 timestamp > C1 timestamp
Problem: Communication Pattern Unclear
Symptom: Don't know which relation type to use
Solution: Decision Tree:
What protocol is used?
├─ HTTP
│ ├─ REST API → "http-rest"
│ ├─ GraphQL → "http-graphql"
│ └─ Generic → "http"
│
├─ gRPC → "grpc"
│
├─ WebSocket → "websocket"
│
├─ Database
│ ├─ Read only → "database-read"
│ ├─ Write only → "database-write"
│ └─ Both → "database-query"
│
├─ Cache
│ ├─ Read only → "cache-read"
│ ├─ Write only → "cache-write"
│ └─ Both → "cache-read-write"
│
├─ Message Queue
│ ├─ Publish → "message-publish"
│ └─ Subscribe → "message-subscribe"
│
└─ File Storage
├─ Read → "file-read"
├─ Write → "file-write"
└─ Both → "file-read-write"
Examples:
- SPA calls API →
http-rest - API reads database →
database-query - API publishes to queue →
message-publish - Worker consumes queue →
message-subscribe - API caches responses →
cache-read-write
Problem: Monorepo vs Microservices Detection
Symptom: Unclear how many containers exist in a monorepo
Solution:
Check for Multiple Package Files
# Find all package.json files (excluding node_modules)
find . -name "package.json" -not -path "*/node_modules/*"
# If multiple found, check structure
ls -la packages/ apps/ services/
Check for Workspace Configuration
# Check for monorepo tools
cat package.json | jq '.workspaces' # npm/yarn workspaces
cat lerna.json # Lerna
cat nx.json # Nx
cat pnpm-workspace.yaml # pnpm
Identify Deployable Units
Rule: Each independently deployable package = 1 container
Example: Nx Monorepo
monorepo/
├── apps/
│ ├── frontend/ → C2 Container (React SPA)
│ ├── api/ → C2 Container (NestJS API)
│ └── admin/ → C2 Container (Admin SPA)
├── libs/
│ ├── ui-components/ → C3 Component (shared library)
│ ├── data-access/ → C3 Component (shared library)
│ └── utils/ → C3 Component (shared library)
Result: 3 containers, 3 shared libraries (C3)
Problem: Serverless Function Granularity
Symptom: Unclear if each Lambda function is a container
Solution:
Option 1: Single Container (Recommended)
Treat all Lambda functions as one container if:
- Deployed together (single serverless.yml)
- Share same codebase
- Same runtime and dependencies
Example:
# serverless.yml
functions:
getUser: ...
createUser: ...
updateUser: ...
deleteUser: ...
→ One Container: "User API Lambda Functions"
Option 2: Multiple Containers
Only split into multiple containers if:
- Deployed independently (separate serverless.yml files)
- Different runtimes or dependencies
- Owned by different teams
Example:
services/
├── user-service/serverless.yml → C2 Container
├── order-service/serverless.yml → C2 Container
└── payment-service/serverless.yml → C2 Container
Rule: Deployment boundary = container boundary
Quick Reference Checklists
Container Type Checklist
spa- Single-page application (browser)mobile-app- iOS/Android applicationdesktop-app- Desktop applicationapiorapp-server- Backend API serverweb-server- Web server, reverse proxydatabase- Relational or NoSQL databasecache- In-memory cache (Redis, Memcached)message-broker- Message queue/event streamingworker- Background job processorfile-storage- Object storage, file system
Technology Detection Checklist
- Primary Language - JavaScript, TypeScript, Python, Java, etc.
- Framework - React, Express, Django, Spring Boot, etc.
- Version - Always include version numbers from manifests
- Key Libraries - List important dependencies with purpose
Runtime Environment Checklist
- Environment - browser / server / mobile / cloud / edge
- Platform - OS, runtime version, supported browsers
- Containerized - true / false
- Container Technology - Docker, Kubernetes (if containerized)
- Deployment Model - Standalone, replicated, serverless
Communication Checklist
- Protocol - HTTP, gRPC, WebSocket, database, message queue
- Direction - unidirectional / bidirectional
- Authentication - JWT, OAuth, API key, none
- Format - JSON, Protobuf, XML, binary
Validation Checklist
- All required fields present (system_id, technology, runtime)
- Timestamp newer than c1-systems.json
- Valid container types used
- Communication patterns documented
- Observations categorized (8 categories)
- Evidence provided for observations
FORBIDDEN Rationalizations (Automatic Validation Failures)
When under pressure, you may be tempted to skip C2 methodology rules. These rationalizations are always wrong and trigger automatic validation failures:
RULE: If your thinking matches ANY of these rationalizations, STOP immediately. You are about to violate C2 methodology. Do not proceed without addressing the actual requirement.
Consequences:
- Each rationalization triggers a CRITICAL severity observation
- Validation scripts will fail with explicit error messages
- Work containing these violations will be rejected
| Rationalization | Why It's Wrong |
|---|---|
| "Versions can be refined later" | Creates technical debt, violates methodology. Do it right now. |
| "Reading package.json is line-by-line analysis" | Manifest files are metadata, not code. Version detection is standard C2. |
| "Version numbers get outdated anyway" | So does all documentation - not an excuse to skip standards. |
| "Generic names are honest about current state" | Generic names violate C2 standards. Period. |
| "Tech lead/senior approved it" | Authority doesn't override methodology. Push back professionally. |
| "Each module is important" | Important ≠ C2 container. Many important things are C3 components. |
| "Team needs granularity" | Then do C3 analysis. Don't pollute C2. |
| "Good enough for now" | Standards exist for consistency across all analysis. |
| "Being pragmatic not dogmatic" | Pragmatism ≠ violating definitions. The methodology IS pragmatic. |
| "We can add more detail later" | "Later" never comes. Do it correctly now. |
| "The spirit of the rule allows this" | Violating the letter IS violating the spirit. |
| "This case is different" | No it isn't. Apply the same standards uniformly. |
If you catch yourself thinking any of these - STOP. Follow the methodology.
Still Having Issues?
Enable Debug Mode
# Run validation with verbose output
python scripts/validate-c2-containers.py --verbose c2-containers.json
# Check for specific issues
python scripts/validate-c2-containers.py --check-timestamps c2-containers.json
Common Debug Commands
# Validate JSON syntax
cat c2-containers.json | jq . > /dev/null && echo "Valid JSON" || echo "Invalid JSON"
# Check required fields
cat c2-containers.json | jq '.containers[] | select(.technology.primary_language == null)'
# Verify timestamps
cat c2-containers.json | jq '.metadata.timestamp, .metadata.parent.timestamp'
# List all container IDs
cat c2-containers.json | jq '.containers[].id'
Get Help
- Review CLAUDE.md workflow guide
- Check ${CLAUDE_PLUGIN_ROOT}/validation/templates/ for examples
- Compare with c4model-c1 successful pattern
- Ask user for clarification on unclear cases