← Back to DevBytes

Engineering Metrics: DORA and SPACE Frameworks

Engineering Metrics: DORA and SPACE Frameworks

Modern software engineering organizations rely on data-driven insights to measure team performance, identify bottlenecks, and continuously improve delivery processes. Two complementary frameworks have emerged as industry standards: DORA (DevOps Research and Assessment) and SPACE. This tutorial covers both frameworks in depth, provides practical implementation examples, and explores how to integrate them into your engineering workflows.

What Is the DORA Framework?

The DORA framework was developed by the DevOps Research and Assessment team at Google Cloud after years of surveying thousands of engineering teams. It identifies four key metrics that correlate strongly with software delivery performance and organizational success:

Why DORA Matters

These four metrics provide a balanced view of both velocity (deployment frequency and lead time) and stability (MTTR and change failure rate). Research shows that teams optimizing for all four metrics simultaneously achieve higher levels of reliability, faster time-to-market, and greater job satisfaction. Elite-performing teams deploy on demand, achieve lead times under one hour, recover from incidents in under an hour, and maintain change failure rates below 5%.

Implementing DORA Metrics: Practical Code Examples

1. Collecting Deployment Frequency

Deployment frequency can be tracked by querying your CI/CD pipeline events or deployment logs. Below is a Python script that calculates deployment frequency from a simulated deployment log stored as JSON:


import json
from datetime import datetime, timedelta
from collections import defaultdict

# Sample deployment log (in practice, pull this from your CI/CD API)
deployment_log = [
    {"timestamp": "2025-01-10T08:30:00Z", "environment": "production", "status": "success"},
    {"timestamp": "2025-01-10T14:15:00Z", "environment": "production", "status": "success"},
    {"timestamp": "2025-01-11T09:00:00Z", "environment": "production", "status": "success"},
    {"timestamp": "2025-01-11T16:45:00Z", "environment": "production", "status": "failed"},
    {"timestamp": "2025-01-12T10:00:00Z", "environment": "production", "status": "success"},
    {"timestamp": "2025-01-13T08:30:00Z", "environment": "production", "status": "success"},
    {"timestamp": "2025-01-13T14:00:00Z", "environment": "production", "status": "success"},
]

def calculate_deployment_frequency(deployments, target_env="production"):
    """Calculate daily deployment frequency for a given environment."""
    daily_counts = defaultdict(int)
    for deploy in deployments:
        if deploy["environment"] == target_env and deploy["status"] == "success":
            date = datetime.fromisoformat(deploy["timestamp"].replace("Z", "+00:00")).date()
            daily_counts[date] += 1
    
    total_days = len(daily_counts)
    total_deployments = sum(daily_counts.values())
    avg_frequency = total_deployments / total_days if total_days > 0 else 0
    
    print(f"Average daily deployment frequency: {avg_frequency:.2f}")
    print(f"Total successful deployments: {total_deployments} over {total_days} days")
    return avg_frequency

calculate_deployment_frequency(deployment_log)

2. Calculating Lead Time for Changes

Lead time is measured from the moment a commit is made to when it reaches production. This requires correlating commit timestamps with deployment timestamps. Here is a script that computes lead time using a simulated commit-to-deploy mapping:


from datetime import datetime, timedelta
import statistics

# Sample data: each record links a commit to its production deployment
commit_to_deploy = [
    {"commit_hash": "abc123", "commit_time": "2025-01-12T09:00:00Z", "deploy_time": "2025-01-12T10:30:00Z"},
    {"commit_hash": "def456", "commit_time": "2025-01-12T11:00:00Z", "deploy_time": "2025-01-12T12:45:00Z"},
    {"commit_hash": "ghi789", "commit_time": "2025-01-12T13:00:00Z", "deploy_time": "2025-01-12T14:10:00Z"},
    {"commit_hash": "jkl012", "commit_time": "2025-01-12T15:00:00Z", "deploy_time": "2025-01-12T15:55:00Z"},
    {"commit_hash": "mno345", "commit_time": "2025-01-12T16:30:00Z", "deploy_time": "2025-01-12T18:20:00Z"},
]

def calculate_lead_time(records):
    """Calculate lead time in minutes for each commit-to-deploy pair."""
    lead_times = []
    for record in records:
        commit_dt = datetime.fromisoformat(record["commit_time"].replace("Z", "+00:00"))
        deploy_dt = datetime.fromisoformat(record["deploy_time"].replace("Z", "+00:00"))
        delta_minutes = (deploy_dt - commit_dt).total_seconds() / 60
        lead_times.append(delta_minutes)
        print(f"Commit {record['commit_hash']}: lead time = {delta_minutes:.0f} minutes")
    
    p50 = statistics.median(lead_times)
    p95 = sorted(lead_times)[int(len(lead_times) * 0.95) - 1] if len(lead_times) > 1 else lead_times[0]
    
    print(f"\nMedian lead time (P50): {p50:.0f} minutes")
    print(f"P95 lead time: {p95:.0f} minutes")
    return lead_times

calculate_lead_time(commit_to_deploy)

3. Measuring Mean Time to Restore (MTTR)

MTTR tracks how quickly your team resolves incidents. The following script processes incident data to compute MTTR:


from datetime import datetime
import statistics

# Sample incident records with start and resolution times
incidents = [
    {"id": "INC-001", "severity": "critical", "start_time": "2025-01-15T14:00:00Z", "resolution_time": "2025-01-15T14:42:00Z"},
    {"id": "INC-002", "severity": "high", "start_time": "2025-01-15T16:00:00Z", "resolution_time": "2025-01-15T17:15:00Z"},
    {"id": "INC-003", "severity": "medium", "start_time": "2025-01-16T09:30:00Z", "resolution_time": "2025-01-16T10:05:00Z"},
    {"id": "INC-004", "severity": "critical", "start_time": "2025-01-16T13:00:00Z", "resolution_time": "2025-01-16T14:30:00Z"},
    {"id": "INC-005", "severity": "high", "start_time": "2025-01-17T08:00:00Z", "resolution_time": "2025-01-17T08:55:00Z"},
]

def calculate_mttr(incident_list):
    """Calculate Mean Time to Restore from incident data."""
    restore_times = []
    for inc in incident_list:
        start = datetime.fromisoformat(inc["start_time"].replace("Z", "+00:00"))
        end = datetime.fromisoformat(inc["resolution_time"].replace("Z", "+00:00"))
        minutes = (end - start).total_seconds() / 60
        restore_times.append(minutes)
        print(f"{inc['id']} ({inc['severity']}): restored in {minutes:.0f} minutes")
    
    mttr = statistics.mean(restore_times)
    median_ttr = statistics.median(restore_times)
    
    print(f"\nMean Time to Restore (MTTR): {mttr:.0f} minutes")
    print(f"Median Time to Restore: {median_ttr:.0f} minutes")
    
    # Breakdown by severity
    severity_groups = {}
    for inc in incident_list:
        severity_groups.setdefault(inc["severity"], []).append(
            (datetime.fromisoformat(inc["resolution_time"].replace("Z", "+00:00")) - 
             datetime.fromisoformat(inc["start_time"].replace("Z", "+00:00"))).total_seconds() / 60
        )
    
    for severity, times in severity_groups.items():
        print(f"  MTTR for {severity}: {statistics.mean(times):.0f} minutes")
    
    return mttr

calculate_mttr(incidents)

4. Computing Change Failure Rate

Change failure rate is the ratio of deployments that cause incidents to total deployments. This script demonstrates the calculation:


from datetime import datetime

# All deployments in a given period
all_deployments = [
    {"id": "DEP-100", "timestamp": "2025-01-20T10:00:00Z", "caused_incident": False},
    {"id": "DEP-101", "timestamp": "2025-01-20T14:00:00Z", "caused_incident": True},
    {"id": "DEP-102", "timestamp": "2025-01-21T09:00:00Z", "caused_incident": False},
    {"id": "DEP-103", "timestamp": "2025-01-21T11:00:00Z", "caused_incident": False},
    {"id": "DEP-104", "timestamp": "2025-01-21T15:00:00Z", "caused_incident": True},
    {"id": "DEP-105", "timestamp": "2025-01-22T08:30:00Z", "caused_incident": False},
    {"id": "DEP-106", "timestamp": "2025-01-22T12:00:00Z", "caused_incident": False},
    {"id": "DEP-107", "timestamp": "2025-01-22T16:00:00Z", "caused_incident": False},
    {"id": "DEP-108", "timestamp": "2025-01-23T10:00:00Z", "caused_incident": False},
    {"id": "DEP-109", "timestamp": "2025-01-23T14:00:00Z", "caused_incident": True},
]

def calculate_change_failure_rate(deployments):
    """Calculate the percentage of deployments that result in incidents."""
    total = len(deployments)
    failures = sum(1 for d in deployments if d["caused_incident"])
    rate = (failures / total) * 100 if total > 0 else 0
    
    print(f"Total deployments: {total}")
    print(f"Failed deployments: {failures}")
    print(f"Change Failure Rate: {rate:.1f}%")
    
    # Categorize performance based on DORA benchmarks
    if rate <= 5:
        level = "Elite (≤5%)"
    elif rate <= 10:
        level = "High (≤10%)"
    elif rate <= 15:
        level = "Medium (≤15%)"
    else:
        level = "Low (>15%)"
    
    print(f"Performance level: {level}")
    return rate

calculate_change_failure_rate(all_deployments)

The SPACE Framework

While DORA focuses narrowly on delivery pipeline metrics, the SPACE framework (developed by GitHub researchers and Microsoft) provides a broader, more holistic view of developer productivity and team health. SPACE stands for:

Why SPACE Matters

SPACE acknowledges that engineering productivity cannot be reduced to a single number. A team with high deployment frequency might be burning out developers or sacrificing code quality. By measuring across all five dimensions, organizations gain a nuanced understanding of team health and can spot negative trade-offs before they become systemic problems.

Implementing SPACE Metrics

S — Satisfaction: Developer Survey Pipeline

Satisfaction is best measured through regular, anonymous surveys. Below is a simple survey processing script that calculates satisfaction scores:


import json
import statistics

# Sample survey responses (1-5 Likert scale)
survey_responses = [
    {"respondent_id": 1, "tool_satisfaction": 4, "workflow_satisfaction": 3, "team_collaboration": 5, "work_life_balance": 4},
    {"respondent_id": 2, "tool_satisfaction": 5, "workflow_satisfaction": 4, "team_collaboration": 4, "work_life_balance": 3},
    {"respondent_id": 3, "tool_satisfaction": 3, "workflow_satisfaction": 2, "team_collaboration": 4, "work_life_balance": 5},
    {"respondent_id": 4, "tool_satisfaction": 4, "workflow_satisfaction": 5, "team_collaboration": 3, "work_life_balance": 4},
    {"respondent_id": 5, "tool_satisfaction": 2, "workflow_satisfaction": 3, "team_collaboration": 4, "work_life_balance": 3},
]

def compute_satisfaction_scores(responses):
    """Calculate average satisfaction across dimensions."""
    dimensions = ["tool_satisfaction", "workflow_satisfaction", "team_collaboration", "work_life_balance"]
    
    for dim in dimensions:
        scores = [r[dim] for r in responses]
        avg = statistics.mean(scores)
        print(f"{dim}: average = {avg:.2f} (min={min(scores)}, max={max(scores)})")
    
    # Overall composite satisfaction score
    all_scores = []
    for r in responses:
        respondent_avg = statistics.mean([r[d] for d in dimensions])
        all_scores.append(respondent_avg)
    
    overall_satisfaction = statistics.mean(all_scores)
    print(f"\nOverall composite satisfaction: {overall_satisfaction:.2f}/5.00")
    
    # Detect concerning signals
    for dim in dimensions:
        scores = [r[dim] for r in responses]
        low_count = sum(1 for s in scores if s <= 2)
        if low_count > 0:
            print(f"  ⚠ {dim}: {low_count} respondent(s) scored ≤ 2 — investigate")

compute_satisfaction_scores(survey_responses)

P — Performance: Quality Metrics Dashboard Query

Performance metrics include code quality, reliability, and user-facing outcomes. This example shows how to aggregate performance indicators from multiple sources:


from datetime import datetime, timedelta
import json

# Simulated performance data from various sources
performance_data = {
    "code_quality": {
        "unit_test_coverage_percent": 87,
        "integration_test_pass_rate_percent": 96,
        "static_analysis_warnings": 34,
        "code_review_acceptance_rate_percent": 92
    },
    "reliability": {
        "uptime_percent": 99.97,
        "p99_latency_ms": 245,
        "error_rate_percent": 0.03,
        "successful_deployments_percent": 94
    },
    "user_satisfaction": {
        "nps_score": 45,
        "support_tickets_weekly": 12,
        "p0_bugs_open": 3,
        "p1_bugs_open": 8
    }
}

def evaluate_performance_metrics(data):
    """Assess performance across quality, reliability, and user satisfaction."""
    
    # Code quality assessment
    quality = data["code_quality"]
    quality_score = 0
    if quality["unit_test_coverage_percent"] >= 80:
        quality_score += 25
        print(f"✓ Test coverage: {quality['unit_test_coverage_percent']}% (threshold met)")
    else:
        print(f"✗ Test coverage: {quality['unit_test_coverage_percent']}% (below 80% target)")
    
    if quality["integration_test_pass_rate_percent"] >= 95:
        quality_score += 25
        print(f"✓ Integration test pass rate: {quality['integration_test_pass_rate_percent']}%")
    
    if quality["static_analysis_warnings"] <= 50:
        quality_score += 25
        print(f"✓ Static analysis warnings: {quality['static_analysis_warnings']} (under 50)")
    
    if quality["code_review_acceptance_rate_percent"] >= 90:
        quality_score += 25
        print(f"✓ Code review acceptance rate: {quality['code_review_acceptance_rate_percent']}%")
    
    print(f"Code quality score: {quality_score}/100")
    
    # Reliability assessment
    reliability = data["reliability"]
    print(f"\nReliability indicators:")
    print(f"  Uptime: {reliability['uptime_percent']}%")
    print(f"  P99 latency: {reliability['p99_latency_ms']}ms")
    print(f"  Error rate: {reliability['error_rate_percent']}%")
    
    # User satisfaction indicators
    user = data["user_satisfaction"]
    print(f"\nUser satisfaction indicators:")
    print(f"  NPS score: {user['nps_score']}")
    print(f"  Open P0 bugs: {user['p0_bugs_open']}")
    print(f"  Open P1 bugs: {user['p1_bugs_open']}")
    
    return quality_score

evaluate_performance_metrics(performance_data)

A — Activity: Engineering Activity Log Analyzer

Activity metrics track the volume of engineering work. This script analyzes activity data from a simulated GitHub-style event log:


from datetime import datetime, timedelta
from collections import defaultdict

# Simulated engineering activity log
activity_log = [
    {"date": "2025-01-20", "user": "alice", "event": "commit", "count": 8},
    {"date": "2025-01-20", "user": "bob", "event": "commit", "count": 6},
    {"date": "2025-01-20", "user": "alice", "event": "pull_request_created", "count": 3},
    {"date": "2025-01-20", "user": "charlie", "event": "code_review_completed", "count": 7},
    {"date": "2025-01-21", "user": "alice", "event": "commit", "count": 10},
    {"date": "2025-01-21", "user": "bob", "event": "commit", "count": 4},
    {"date": "2025-01-21", "user": "charlie", "event": "pull_request_merged", "count": 5},
    {"date": "2025-01-21", "user": "alice", "event": "code_review_completed", "count": 4},
    {"date": "2025-01-22", "user": "bob", "event": "commit", "count": 7},
    {"date": "2025-01-22", "user": "charlie", "event": "commit", "count": 9},
    {"date": "2025-01-22", "user": "alice", "event": "pull_request_merged", "count": 6},
    {"date": "2025-01-22", "user": "bob", "event": "code_review_completed", "count": 5},
]

def analyze_activity(events):
    """Summarize engineering activity by type and contributor."""
    
    # Aggregate by event type
    event_totals = defaultdict(int)
    user_activity = defaultdict(lambda: defaultdict(int))
    
    for entry in events:
        event_totals[entry["event"]] += entry["count"]
        user_activity[entry["user"]][entry["event"]] += entry["count"]
    
    print("=== Activity by Event Type ===")
    for event, total in sorted(event_totals.items(), key=lambda x: x[1], reverse=True):
        print(f"  {event}: {total}")
    
    print("\n=== Activity by Contributor ===")
    for user, activities in sorted(user_activity.items()):
        total = sum(activities.values())
        print(f"  {user}: {total} total actions")
        for event, count in activities.items():
            print(f"    - {event}: {count}")
    
    # Detect potential imbalances
    print("\n=== Activity Balance Check ===")
    user_totals = {user: sum(acts.values()) for user, acts in user_activity.items()}
    max_actions = max(user_totals.values())
    min_actions = min(user_totals.values())
    
    if max_actions > min_actions * 3:
        print("  ⚠ High activity disparity detected between contributors")
        print(f"    Max: {max_actions}, Min: {min_actions}")
    else:
        print("  ✓ Activity distribution is reasonably balanced")

analyze_activity(activity_log)

C — Communication & E — Efficiency: Combined Pipeline Analyzer

Communication and Efficiency metrics can be derived from collaboration tool data and CI/CD pipeline statistics. This combined script measures both dimensions:


from datetime import datetime, timedelta

# Sample collaboration and pipeline data
collaboration_data = {
    "meetings": {
        "avg_weekly_meeting_hours_per_dev": 8.5,
        "standup_duration_minutes": 15,
        "adhoc_meetings_per_week": 4,
        "focus_time_percent": 62
    },
    "documentation": {
        "wiki_pages_updated_weekly": 12,
        "design_docs_reviewed_weekly": 5,
        "onboarding_docs_up_to_date": True
    },
    "knowledge_sharing": {
        "internal_tech_talks_monthly": 3,
        "mentoring_sessions_weekly": 6,
        "code_review_threads_avg_depth": 4.2
    }
}

pipeline_data = {
    "ci_pipeline": {
        "avg_build_time_minutes": 12,
        "avg_test_suite_minutes": 8,
        "pipeline_success_rate_percent": 88,
        "flaky_test_percentage": 3.5
    },
    "cd_pipeline": {
        "avg_deploy_time_minutes": 5,
        "rollback_rate_percent": 4,
        "manual_intervention_required_percent": 12
    },
    "developer_experience": {
        "local_build_time_minutes": 3,
        "hot_reload_latency_seconds": 0.8,
        "time_to_first_review_hours": 6.5
    }
}

def assess_communication_and_efficiency(collab, pipeline):
    """Evaluate communication health and pipeline efficiency."""
    
    print("=== Communication Metrics ===")
    
    # Meeting health
    meeting_hours = collab["meetings"]["avg_weekly_meeting_hours_per_dev"]
    focus_percent = collab["meetings"]["focus_time_percent"]
    
    print(f"Weekly meeting hours per dev: {meeting_hours}h")
    print(f"Focus time: {focus_percent}%")
    
    if meeting_hours > 12:
        print("  ⚠ High meeting load — consider async standups and no-meeting days")
    elif meeting_hours < 4:
        print("  ⚠ Very low meeting load — check for insufficient coordination")
    else:
        print("  ✓ Meeting load is within healthy range")
    
    # Documentation health
    doc_updates = collab["documentation"]["wiki_pages_updated_weekly"]
    print(f"\nWiki pages updated weekly: {doc_updates}")
    print(f"Onboarding docs current: {'✓' if collab['documentation']['onboarding_docs_up_to_date'] else '✗ Needs update'}")
    
    # Knowledge sharing
    print(f"\nInternal tech talks (monthly): {collab['knowledge_sharing']['internal_tech_talks_monthly']}")
    print(f"Avg code review thread depth: {collab['knowledge_sharing']['code_review_threads_avg_depth']}")
    print(f"Mentoring sessions (weekly): {collab['knowledge_sharing']['mentoring_sessions_weekly']}")
    
    print("\n=== Efficiency Metrics ===")
    
    ci = pipeline["ci_pipeline"]
    cd = pipeline["cd_pipeline"]
    dx = pipeline["developer_experience"]
    
    total_ci_time = ci["avg_build_time_minutes"] + ci["avg_test_suite_minutes"]
    print(f"Total CI pipeline time: {total_ci_time} minutes")
    print(f"Pipeline success rate: {ci['pipeline_success_rate_percent']}%")
    print(f"Flaky test percentage: {ci['flaky_test_percentage']}%")
    
    if ci["flaky_test_percentage"] > 5:
        print("  ⚠ Flaky test rate above 5% — prioritize test stabilization")
    
    print(f"\nAvg deploy time: {cd['avg_deploy_time_minutes']} minutes")
    print(f"Rollback rate: {cd['rollback_rate_percent']}%")
    print(f"Manual intervention required: {cd['manual_intervention_required_percent']}% of deployments")
    
    print(f"\nLocal build time: {dx['local_build_time_minutes']} minutes")
    print(f"Hot reload latency: {dx['hot_reload_latency_seconds']} seconds")
    print(f"Time to first code review: {dx['time_to_first_review_hours']} hours")
    
    # Efficiency score calculation
    efficiency_score = 100
    
    if total_ci_time > 30:
        efficiency_score -= 15
        print("  -15: CI pipeline exceeds 30 minutes")
    if ci["flaky_test_percentage"] > 5:
        efficiency_score -= 10
        print("  -10: Flaky tests above threshold")
    if cd["manual_intervention_required_percent"] > 10:
        efficiency_score -= 15
        print("  -15: High manual intervention in deployments")
    if dx["time_to_first_review_hours"] > 24:
        efficiency_score -= 10
        print("  -10: Time to first review exceeds 24 hours")
    
    print(f"\nEfficiency score: {efficiency_score}/100")

assess_communication_and_efficiency(collaboration_data, pipeline_data)

Combining DORA and SPACE: A Unified Dashboard

The true power of these frameworks emerges when you combine them. DORA tells you how well your delivery pipeline is performing; SPACE tells you whether that performance is sustainable and aligned with developer experience. Below is a unified reporting script that integrates both frameworks into a single dashboard output:


from datetime import datetime
import statistics
import json

class EngineeringMetricsDashboard:
    """
    Unified dashboard that combines DORA and SPACE metrics
    into a single comprehensive engineering health report.
    """
    
    def __init__(self, team_name, reporting_period):
        self.team_name = team_name
        self.reporting_period = reporting_period
        self.dora_metrics = {}
        self.space_metrics = {}
    
    def load_dora_data(self, deployment_frequency, lead_time_minutes, 
                        mttr_minutes, change_failure_rate_percent):
        """Ingest DORA metrics."""
        self.dora_metrics = {
            "deployment_frequency_daily": deployment_frequency,
            "lead_time_median_minutes": lead_time_minutes,
            "mttr_minutes": mttr_minutes,
            "change_failure_rate_percent": change_failure_rate_percent
        }
    
    def load_space_data(self, satisfaction_score, performance_score,
                         activity_summary, communication_score, efficiency_score):
        """Ingest SPACE metrics."""
        self.space_metrics = {
            "satisfaction": satisfaction_score,
            "performance": performance_score,
            "activity": activity_summary,
            "communication": communication_score,
            "efficiency": efficiency_score
        }
    
    def classify_dora_performance(self):
        """Classify team into DORA performance category."""
        d = self.dora_metrics
        scores = []
        
        # Deployment frequency scoring
        if d["deployment_frequency_daily"] >= 1:
            scores.append(4)  # Elite: on-demand / daily
        elif d["deployment_frequency_daily"] >= 0.14:  # Weekly
            scores.append(3)  # High
        elif d["deployment_frequency_daily"] >= 0.03:  # Monthly
            scores.append(2)  # Medium
        else:
            scores.append(1)  # Low
        
        # Lead time scoring
        lt = d["lead_time_median_minutes"]
        if lt <= 60:
            scores.append(4)
        elif lt <= 1440:  # One day
            scores.append(3)
        elif lt <= 10080:  # One week
            scores.append(2)
        else:
            scores.append(1)
        
        # MTTR scoring
        mttr = d["mttr_minutes"]
        if mttr <= 60:
            scores.append(4)
        elif mttr <= 1440:
            scores.append(3)
        elif mttr <= 10080:
            scores.append(2)
        else:
            scores.append(1)
        
        # Change failure rate scoring
        cfr = d["change_failure_rate_percent"]
        if cfr <= 5:
            scores.append(4)
        elif cfr <= 10:
            scores.append(3)
        elif cfr <= 15:
            scores.append(2)
        else:
            scores.append(1)
        
        avg_score = statistics.mean(scores)
        if avg_score >= 3.5:
            return "Elite"
        elif avg_score >= 2.5:
            return "High"
        elif avg_score >= 1.5:
            return "Medium"
        else:
            return "Low"
    
    def generate_report(self):
        """Generate the full unified engineering metrics report."""
        performance_level = self.classify_dora_performance()
        
        report = f"""
═══════════════════════════════════════════════════
  ENGINEERING METRICS DASHBOARD
  Team: {self.team_name}
  Period: {self.reporting_period}
  Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
═══════════════════════════════════════════════════

>>> DORA METRICS (Delivery Performance)
  Deployment Frequency:        {self.dora_metrics['deployment_frequency_daily']:.2f} per day
  Lead Time (median):          {self.dora_metrics['lead_time_median_minutes']:.0f} minutes
  Mean Time to Restore:        {self.dora_metrics['mttr_minutes']:.0f} minutes
  Change Failure Rate:         {self.dora_metrics['change_failure_rate_percent']:.1f}%
  
  Overall DORA Classification: {performance_level}

>>> SPACE METRICS (Developer Experience & Productivity)
  Satisfaction:                {self.space_metrics['satisfaction']:.1f}/5.0
  Performance (Quality):       {self.space_metrics['performance']}/100
  Communication Health:        {self.space_metrics['communication']}/100
  Efficiency:                  {self.space_metrics['efficiency']}/100
  
  Activity Summary:
{self.space_metrics['activity']}

═══════════════════════════════════════════════════
  RECOMMENDATIONS
═══════════════════════════════════════════════════
"""
        
        # Generate contextual recommendations
        recommendations = []
        d = self.dora_metrics
        s = self.space_metrics
        
        if d["change_failure_rate_percent"] > 10:
            recommendations.append("→ Reduce change failure rate: implement more rigorous testing and canary deployments")
        
        if d["mttr_minutes"] > 1440:
            recommendations.append("→ Improve MTTR: invest in monitoring, runbooks, and incident response automation")
        
        if d["lead_time_median_minutes"] > 1440:
            recommendations.append("→ Reduce lead time: break down large changes, optimize CI pipeline, increase

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles