Dependency Mapping

Production engineering guide for dependency mapping covering patterns, implementation strategies, and operational best practices.

Dependency mapping identifies and documents relationships between services, systems, and components that affect each other's operation. This process matters when planning changes, troubleshooting failures, or understanding impact of incidents. The goal is to create a visual or structured representation of how systems depend on each other, enabling better decision-making during deployments, capacity planning, and incident response.

Understanding Dependencies

Dependencies are relationships where one system's operation requires another system's availability or correct behavior. A service depends on a database if it cannot function without the database being available. A dependency is not just a network connection; it's a functional relationship that affects uptime, performance, and correctness.

Mapping with Kubernetes

When working in Kubernetes environments, use kubectl to inspect dependencies between workloads:

kubectl get pods -o jsonpath='{.items[*].spec.containers[*].image}' | tr ' ' '\n' | sort | uniq -c

This command shows all container images used across pods, helping identify shared dependencies. Dependencies are not just about containers; they're also about resource requirements.

kubectl get deployment <deployment-name> -o jsonpath='{.spec.template.spec.containers[*].resources}'

Use this to check if deployments specify resource limits that might cause cascading failures during high load.

Mapping with Docker Compose

In Docker Compose environments, dependencies are defined in the docker-compose.yml file:

version: '3.8'
services:
  web:
    image: nginx:latest
    depends_on:
      - database
      - cache
  database:
    image: postgres:13
    environment:
      POSTGRES_DB: myapp
  cache:
    image: redis:6-alpine

The depends_on directive creates startup dependencies, but does not guarantee that the service is ready to receive requests. A web service starting before the database is fully initialized can still fail.

Identifying Service Dependencies

Network-Level Dependencies

Network dependencies are often expressed through DNS resolution, port availability, and protocol requirements:

# Check if a service responds on a specific port
nc -zv <hostname> <port>

# Monitor connection establishment times
timeout 5 telnet <hostname> <port>

Use ss or netstat to verify listening services:

ss -tuln | grep :80

A service that binds to port 80 but fails to respond due to incorrect configuration often produces no error logs until a client attempts connection.

Database Dependencies

Database dependencies require examining connection strings and query patterns:

-- Check active connections
SELECT * FROM pg_stat_activity WHERE state = 'active';

-- Identify long-running queries
SELECT pid, query, now() - pg_stat_activity.query_start AS duration
FROM pg_stat_activity
WHERE state = 'active' AND now() - pg_stat_activity.query_start > interval '5 minutes';

Database dependencies can fail silently if a connection pool exhausts. The application may appear healthy while all database connections are in use.

External API Dependencies

When services depend on external APIs, check the HTTP response codes and timing:

curl -w "@curl-format.txt" -o /dev/null -s https://api.example.com/health

Where curl-format.txt contains:

     time_namelookup:  %{time_namelookup}\n
        time_connect:  %{time_connect}\n
     time_appconnect:  %{time_appconnect}\n
    time_pretransfer:  %{time_pretransfer}\n
       time_redirect:  %{time_redirect}\n
  time_starttransfer:  %{time_starttransfer}\n
                     ----------\n
          time_total:  %{time_total}\n

A failing API dependency may return HTTP 200 with a body indicating failure, rather than returning an error code.

Mapping Dependencies in Code

Code-Level Dependencies

Dependencies are often encoded in source code and configuration:

// Example Go service that depends on database
func GetUserData(userID string) (*User, error) {
    db, err := sql.Open("postgres", config.DatabaseURL)
    if err != nil {
        return nil, err
    }
    defer db.Close()
    
    row := db.QueryRow("SELECT name FROM users WHERE id = $1", userID)
    var name string
    err = row.Scan(&name)
    return &User{Name: name}, err
}

Dependencies can be expressed through environment variables or configuration files. When these are missing, the application fails to start or behaves inconsistently.

Configuration Dependencies

Configuration files define how services interact:

# config.yaml
database:
  host: db.example.com
  port: 5432
  name: myapp
  username: appuser
  password: ${DB_PASSWORD}

When a configuration key is missing, services often fall back to default values that are incorrect for the environment. This causes silent failures where systems appear operational but behave incorrectly.

Mapping Dependencies in Infrastructure

Cloud Provider Dependencies

Infrastructure dependencies can be expressed through IAM roles, VPC peering, or service accounts:

# Check if an EC2 instance has proper IAM role attached
curl -s http://169.254.169.254/latest/meta-data/iam/info/

AWS Lambda functions depend on IAM permissions that must be granted to access other services. A missing permission causes a silent failure rather than an explicit error message.

Load Balancer Dependencies

Load balancer dependencies are often misconfigured:

# Check health check endpoints
curl -f http://<loadbalancer-ip>/health

A load balancer may consider a backend healthy even when it's not serving requests correctly. The /health endpoint must return consistent results.

Dependency Failure Patterns

Cascading Failures

Dependencies can cause cascading failures:

# Monitor for dependency timeouts
timeout 10 curl -s http://service-a.example.com/api/endpoint

When service A depends on service B, and service B fails, the failure propagates through service A's circuit breaker or timeout configuration.

Silent Dependency Failures

Some dependencies fail silently:

# Check if a service is actually responding
curl -s http://service.example.com/health | grep -q "healthy" || echo "Health check failed"

A service may be running but return incorrect responses due to misconfigured dependencies.

Resource Exhaustion Dependencies

Dependencies can cause resource exhaustion:

# Monitor memory usage of dependent services
docker stats --no-stream <container-name>

When a service depends on another that consumes excessive resources, the dependent service fails silently.

Dependency Mapping Tools and Techniques

Service Mesh Dependencies

Service mesh tools like Istio or Linkerd can provide dependency visibility:

# Check istio proxy metrics
kubectl get pods -l app=istio-proxy -o jsonpath='{.items[*].status.containerStatuses[*].ready}'

Istio's DestinationRule defines how traffic is routed between services, and misconfiguration here affects dependencies.

Monitoring for Dependency Changes

Monitor dependency changes using:

# Track changes in service versions
kubectl get deployments -o jsonpath='{.items[*].spec.template.spec.containers[*].image}' | sort

Changes in dependencies often occur through image updates or configuration changes that are not tracked properly.

Mapping Strategies

Static Mapping

Static mapping uses code analysis and configuration files:

# Analyze all YAML files for dependency references
grep -r "database" . --include="*.yaml" --include="*.yml"

Static mapping works well for infrastructure-as-code but misses runtime dependencies.

Dynamic Mapping

Dynamic mapping captures actual service interactions:

# Capture network traffic to identify real dependencies
tcpdump -i any -w dependency.pcap host database.example.com

Dynamic mapping reveals unexpected dependencies that static analysis might miss.

Common Mapping Errors

Overlooking Indirect Dependencies

Services may depend on other services indirectly through shared resources:

# Check if services share a common resource
kubectl get persistentvolumeclaims -o jsonpath='{.items[*].spec.volumeName}'

A shared volume or secret may cause cascading failures when it becomes unavailable.

Missing Dependency Tracking

Dependencies without tracking often appear during incidents:

# Log all service dependencies at startup
echo "Service started with dependencies: $(kubectl get pods -l app=service-name -o jsonpath='{.items[*].spec.containers[*].image}')"

Missing dependency tracking leads to situations where engineers cannot quickly identify which services are affected.

Misunderstanding Dependency Types

Dependencies can be functional, resource-based, or temporal:

# Check for resource dependencies
kubectl top pods

A service may depend on CPU resources rather than just network connectivity. When resource limits are reached, the service fails in unexpected ways.

Validation and Testing

Testing Dependencies

Test dependencies with explicit calls:

# Test database connectivity
psql -h <host> -U <user> -c "SELECT 1;" > /dev/null 2>&1 && echo "Database OK" || echo "Database Failed"

Dependencies should be tested as part of deployment verification.

Validating Mapping Accuracy

Validate dependency maps by comparing with actual behavior:

# Monitor actual service interactions
kubectl logs -l app=web-service | grep -E "(dependency|database|cache)"

Actual log output should match the expected dependency relationships in the map.

Documentation and Maintenance

Dependency maps should be maintained as part of the system documentation. When dependencies change, the map must be updated to reflect current relationships:

# Generate dependency documentation
kubectl get services -o jsonpath='{.items[*].metadata.name}' > dependencies.txt

Regular reviews ensure that dependency maps remain accurate and useful for decision-making.

The key is to treat dependencies as first-class citizens in system design, not afterthoughts that appear during incidents.

This page was rewritten on 16 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.