Home▸Articles▸Privacy, Fairness, and Compliance

Model Governance and Compliance

Managing ML models responsibly - model cards, audit trails, regulatory compliance, and governance frameworks for production ML.

mysimulator teamUpdated 2026-01≈ 6 min read▶ Open the simulation

Model governance ensures ML systems are developed, deployed, and monitored responsibly. It covers documentation, approval workflows, audit trails, and regulatory compliance. As ML becomes regulated (EU AI Act, FDA guidance, financial regulations), governance transforms from best practice to legal requirement.

Where It's Used

Governance is essential in regulated and high-stakes domains:

Regulations: EU AI Act, GDPR, ECOA, SR 11-7, FDA guidance, EEOC.

Governance Components

Component Purpose Artefacts
Documentation Record what model does and how Model cards, datasheets
Approval workflows Control deployment decisions Sign-off processes
Audit trails Track all changes and decisions Logs, version history
Risk assessment Evaluate potential harms Risk matrices
Monitoring Ongoing performance and fairness Dashboards, alerts
Incident response Handle failures and issues Playbooks, escalation

Typical Requirements

Aspect Common Requirements
Documentation Model purpose, data, performance, limitations
Versioning All model versions tracked and retrievable
Testing Validation before deployment
Approval Designated approvers for production
Monitoring Ongoing performance and bias tracking
Retention Models and data retained for N years

What You Actually Do

  1. Define model risk tiers - High, medium, low based on impact
  2. Create documentation templates - Model cards, datasheets
  3. Build approval workflows - Who approves what tier
  4. Implement model registry - Central catalogue with metadata
  5. Set up version control - Models, data, code all versioned
  6. Establish testing requirements - Per-tier validation
  7. Deploy monitoring - Performance, fairness, drift
  8. Create audit trails - Immutable logs of all actions
  9. Plan incident response - Playbooks for failures
  10. Train teams - Governance processes and responsibilities

Model Cards

Standard documentation format for ML models:

# model_card.yaml
model_details:
  name: "Credit Risk Scorer v2.3"
  version: "2.3.0"
  type: "Binary classification (XGBoost)"
  owner: "Risk Analytics Team"
  contact: "risk-ml@company.com"
  created: "2026-01-15"
  last_updated: "2026-01-30"

intended_use:
  primary_uses:
    - "Credit limit decisions for existing customers"
  out_of_scope_uses:
    - "New customer acquisition"
    - "Fraud detection"
  users:
    - "Credit decision systems"
    - "Risk analysts"

training_data:
  description: "12 months of credit applications and outcomes"
  size: "2.4M samples"
  features: "145 features after engineering"
  label: "90-day default indicator"
  collection_period: "2025-01 to 2025-12"
  known_limitations:
    - "Under-represents rural customers"
    - "Economic conditions may have shifted"

evaluation:
  metrics:
    - name: "AUC-ROC"
      value: 0.82
      confidence_interval: "[0.81, 0.83]"
    - name: "Gini"
      value: 0.64
  datasets:
    - name: "Holdout test set"
      size: "240K samples"
  fairness_evaluation:
    - attribute: "Age group"
      metric: "Equal opportunity difference"
      value: 0.03
    - attribute: "Gender"
      metric: "Demographic parity difference"
      value: 0.02

ethical_considerations:
  risks:
    - "May perpetuate historical lending biases"
    - "Performance may degrade for demographic shifts"
  mitigations:
    - "Quarterly fairness audits"
    - "Monthly performance monitoring by segment"

caveats_and_recommendations:
  - "Not validated for customers under 21"
  - "Requires manual review for edge cases"
  - "Retrain if AUC drops below 0.78"

Model Registry

from dataclasses import dataclass
from datetime import datetime
from typing import List, Dict, Optional
import json

@dataclass
class ModelVersion:
    version: str
    created_at: datetime
    created_by: str
    model_path: str
    metrics: Dict[str, float]
    status: str  # development, staging, production, retired
    approved_by: Optional[str] = None
    approved_at: Optional[datetime] = None

class ModelRegistry:
    def __init__(self, storage_backend):
        self.storage = storage_backend
        self.models = {}

    def register_model(self, name: str, version: ModelVersion):
        """Register a new model version."""
        if name not in self.models:
            self.models[name] = []
        self.models[name].append(version)
        self._log_event("register", name, version.version)

    def promote_to_staging(self, name: str, version: str, approver: str):
        """Promote model to staging environment."""
        model = self._get_version(name, version)
        if model.status != "development":
            raise ValueError(f"Can only promote from development, current: {model.status}")
        model.status = "staging"
        self._log_event("promote_staging", name, version, approver)

    def promote_to_production(self, name: str, version: str, approver: str):
        """Promote model to production with approval."""
        model = self._get_version(name, version)
        if model.status != "staging":
            raise ValueError("Must pass staging before production")
        model.status = "production"
        model.approved_by = approver
        model.approved_at = datetime.now()
        self._log_event("promote_production", name, version, approver)

    def retire_model(self, name: str, version: str, reason: str):
        """Retire a model version."""
        model = self._get_version(name, version)
        model.status = "retired"
        self._log_event("retire", name, version, reason=reason)

    def get_production_model(self, name: str) -> Optional[ModelVersion]:
        """Get current production model."""
        if name not in self.models:
            return None
        for model in reversed(self.models[name]):
            if model.status == "production":
                return model
        return None

    def _get_version(self, name: str, version: str) -> ModelVersion:
        for model in self.models.get(name, []):
            if model.version == version:
                return model
        raise ValueError(f"Model {name} version {version} not found")

    def _log_event(self, event_type: str, model_name: str, version: str, 
                   approver: str = None, reason: str = None):
        """Immutable audit log."""
        event = {
            "timestamp": datetime.now().isoformat(),
            "event_type": event_type,
            "model_name": model_name,
            "version": version,
            "approver": approver,
            "reason": reason
        }
        self.storage.append_log(event)

Approval Workflows

from enum import Enum
from typing import List

class RiskTier(Enum):
    LOW = "low"       # No human approval needed
    MEDIUM = "medium"  # Team lead approval
    HIGH = "high"      # Committee approval

class ApprovalWorkflow:
    def __init__(self):
        self.approval_rules = {
            RiskTier.LOW: [],
            RiskTier.MEDIUM: ["team_lead"],
            RiskTier.HIGH: ["team_lead", "risk_committee", "legal"]
        }

    def get_required_approvers(self, risk_tier: RiskTier) -> List[str]:
        return self.approval_rules[risk_tier]

    def check_approval_status(self, model_name: str, approvals: List[dict], 
                               risk_tier: RiskTier) -> dict:
        required = set(self.approval_rules[risk_tier])
        approved = {a["role"] for a in approvals if a["decision"] == "approve"}
        pending = required - approved
        rejected = [a for a in approvals if a["decision"] == "reject"]

        return {
            "approved": len(pending) == 0 and len(rejected) == 0,
            "pending_approvers": list(pending),
            "rejected": len(rejected) > 0,
            "rejection_reasons": [r["reason"] for r in rejected]
        }

Audit Trail Requirements

from datetime import datetime
import hashlib
import json

class AuditLog:
    def __init__(self, storage):
        self.storage = storage

    def log_model_event(self, event_type: str, model_id: str, 
                        user: str, details: dict):
        """Create immutable audit entry."""
        entry = {
            "timestamp": datetime.utcnow().isoformat(),
            "event_type": event_type,
            "model_id": model_id,
            "user": user,
            "details": details,
            "previous_hash": self._get_last_hash()
        }
        entry["hash"] = self._compute_hash(entry)
        self.storage.append(entry)

    def _compute_hash(self, entry: dict) -> str:
        content = json.dumps(entry, sort_keys=True, default=str)
        return hashlib.sha256(content.encode()).hexdigest()

    def _get_last_hash(self) -> str:
        last = self.storage.get_last()
        return last["hash"] if last else "genesis"

    def verify_chain(self) -> bool:
        """Verify audit log integrity."""
        entries = self.storage.get_all()
        for i, entry in enumerate(entries):
            expected_prev = entries[i-1]["hash"] if i > 0 else "genesis"
            if entry["previous_hash"] != expected_prev:
                return False
            stored_hash = entry["hash"]
            entry_copy = {k: v for k, v in entry.items() if k != "hash"}
            if self._compute_hash(entry_copy) != stored_hash:
                return False
        return True

Evaluation Metrics

Metric Description Use Case
Documentation coverage % models with complete docs Compliance
Approval latency Time from request to approval Process efficiency
Audit completeness % actions logged Audit readiness
Monitoring coverage % models with active monitoring Risk management

Common Mistakes & Checklist

Production Checklist

Before deployment: - [ ] Model card completed - [ ] Required approvals obtained - [ ] Testing requirements met - [ ] Monitoring configured

After deployment: - [ ] Audit trail verified - [ ] Monitoring active - [ ] Incident response plan in place - [ ] Periodic review scheduled

Tools & Stack

Category Tools
Model Registry MLflow, Weights & Biases, Neptune
Documentation Model Cards Toolkit, Datasheets
Workflow Prefect, Airflow, custom approval systems
Audit Immutable logs, blockchain (regulated)

Mini Case Example

Scenario: Implementing governance for credit decisioning.

Approach: - Three-tier risk classification - Model cards for all production models - Automated testing gates - Committee approval for high-risk

Results: - 100% documentation coverage - Passed regulatory audit - 3-day average approval time (was 3 weeks)

Related Pages

Further Reading

Try it live

Everything above runs in your browser — open AI Governance Policy Impact: Speed-Adjustable Model and change the parameters while it is running. Nothing is installed, nothing is uploaded, the whole model lives in one tab.

▶ Open AI Governance Policy Impact: Speed-Adjustable Model simulation

What did you find?

Add reproduction steps (optional)