{
  "nbformat": 4,
  "nbformat_minor": 5,
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.13.0"
    },
    "blog_metadata": {
      "topic": "Taming GitHub Copilot Spend Without Slowing Developers",
      "slug": "taming-github-copilot-spend-without-slowing-developers",
      "generated_by": "LinkedIn Post Generator + Azure OpenAI",
      "generated_at": "2026-07-30T18:39:04.916Z"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Taming GitHub Copilot Spend Without Slowing Developers\n",
        "\n",
        "This notebook turns the blog post into a hands-on validation workflow using Python. It focuses on practical governance patterns for Copilot spend: visibility first, policy checks before provisioning, reclaim reviews, temporary exceptions, and simple efficiency metrics."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "%pip install -q pandas matplotlib"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "from dataclasses import dataclass\n",
        "from collections import defaultdict\n",
        "from datetime import datetime, timedelta\n",
        "import pandas as pd\n",
        "import matplotlib.pyplot as plt"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Operating model overview\n",
        "\n",
        "The blog argues that Copilot spend becomes a governance problem before it becomes a finance problem. The practical operating model is built around sensible defaults, delegated approvals, fast exceptions, regular reviews, and named owners.\n",
        "\n",
        "Reference flow from the post:\n",
        "\n",
        "Developer requests Copilot -> Policy check -> Assign Business seat or fallback -> Track usage and cost center -> Monthly review -> Reclaim or keep/expand."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 1: Estimate monthly Copilot spend by team and flag low-utilization seats\n",
        "\n",
        "This example validates a simple monthly review loop. It calculates team-level spend from seat counts and flags reclaim candidates when active days fall below a threshold."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "from dataclasses import dataclass\n",
        "\n",
        "@dataclass\n",
        "class Seat:\n",
        "    user: str\n",
        "    team: str\n",
        "    active_days: int\n",
        "\n",
        "PRICE_PER_SEAT = 19\n",
        "LOW_USAGE_THRESHOLD = 5\n",
        "\n",
        "seats = [\n",
        "    Seat(\"ana\", \"platform\", 18),\n",
        "    Seat(\"ben\", \"platform\", 2),\n",
        "    Seat(\"chris\", \"data\", 11),\n",
        "]\n",
        "\n",
        "team_cost = {}\n",
        "reclaim_candidates = []\n",
        "\n",
        "for seat in seats:\n",
        "    team_cost[seat.team] = team_cost.get(seat.team, 0) + PRICE_PER_SEAT\n",
        "    if seat.active_days < LOW_USAGE_THRESHOLD:\n",
        "        reclaim_candidates.append({\n",
        "            \"user\": seat.user,\n",
        "            \"team\": seat.team,\n",
        "            \"active_days\": seat.active_days\n",
        "        })\n",
        "        print(f\"Reclaim candidate: {seat.user} ({seat.active_days} active days)\")\n",
        "\n",
        "print(\"Monthly cost by team:\", team_cost)\n",
        "print(\"Total monthly cost:\", sum(team_cost.values()))\n",
        "\n",
        "pd.DataFrame(reclaim_candidates)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 2: Enforce a simple seat-allocation policy before provisioning\n",
        "\n",
        "This example models the policy check that should happen before access is granted. It approves only teams on the allowlist and only when adding a seat stays within the team's monthly budget."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "approved_teams = {\"platform\", \"data\", \"security\"}\n",
        "monthly_budget_by_team = {\"platform\": 200, \"data\": 100, \"security\": 60}\n",
        "current_seats_by_team = {\"platform\": 8, \"data\": 4, \"security\": 2}\n",
        "price_per_seat = 19\n",
        "\n",
        "def can_assign(team: str) -> bool:\n",
        "    if team not in approved_teams:\n",
        "        return False\n",
        "    projected = (current_seats_by_team.get(team, 0) + 1) * price_per_seat\n",
        "    return projected <= monthly_budget_by_team.get(team, 0)\n",
        "\n",
        "results = []\n",
        "for team in [\"platform\", \"data\", \"sales\"]:\n",
        "    decision = \"APPROVE\" if can_assign(team) else \"DENY\"\n",
        "    results.append({\n",
        "        \"team\": team,\n",
        "        \"current_seats\": current_seats_by_team.get(team, 0),\n",
        "        \"budget\": monthly_budget_by_team.get(team),\n",
        "        \"projected_cost_with_new_seat\": (current_seats_by_team.get(team, 0) + 1) * price_per_seat,\n",
        "        \"decision\": decision,\n",
        "    })\n",
        "    print(f\"{team}: {decision}\")\n",
        "\n",
        "pd.DataFrame(results)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 3: Auto-expire trial seats unless usage justifies keeping them\n",
        "\n",
        "The blog recommends fast exceptions and temporary access with review dates. This example simulates trial seats that expire automatically unless usage is strong enough to justify retention."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "today = datetime.now()\n",
        "trials = [\n",
        "    {\"user\": \"ana\", \"expires\": today + timedelta(days=-1), \"active_days\": 14},\n",
        "    {\"user\": \"ben\", \"expires\": today + timedelta(days=-2), \"active_days\": 3},\n",
        "    {\"user\": \"chris\", \"expires\": today + timedelta(days=7), \"active_days\": 1},\n",
        "]\n",
        "\n",
        "trial_actions = []\n",
        "for trial in trials:\n",
        "    if trial[\"expires\"] < today:\n",
        "        if trial[\"active_days\"] >= 8:\n",
        "            action = f\"Keep seat for {trial['user']}: strong usage\"\n",
        "        else:\n",
        "            action = f\"Remove seat for {trial['user']}: expired and low usage\"\n",
        "        print(action)\n",
        "        trial_actions.append({**trial, \"action\": action})\n",
        "    else:\n",
        "        action = f\"No action for {trial['user']}: not yet expired\"\n",
        "        print(action)\n",
        "        trial_actions.append({**trial, \"action\": action})\n",
        "\n",
        "pd.DataFrame(trial_actions)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 4: Rank teams by cost efficiency using accepted-suggestion outcomes\n",
        "\n",
        "The post emphasizes that a usage spike is not automatically waste. This example adds a simple value lens by comparing accepted suggestions to monthly seat cost, producing a rough efficiency metric for team review."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "teams = [\n",
        "    {\"team\": \"platform\", \"seats\": 8, \"accepted_suggestions\": 420},\n",
        "    {\"team\": \"data\", \"seats\": 4, \"accepted_suggestions\": 110},\n",
        "    {\"team\": \"security\", \"seats\": 2, \"accepted_suggestions\": 95},\n",
        "]\n",
        "\n",
        "price_per_seat = 19\n",
        "rows = []\n",
        "for item in teams:\n",
        "    monthly_cost = item[\"seats\"] * price_per_seat\n",
        "    efficiency = item[\"accepted_suggestions\"] / monthly_cost\n",
        "    rows.append({\n",
        "        \"team\": item[\"team\"],\n",
        "        \"seats\": item[\"seats\"],\n",
        "        \"accepted_suggestions\": item[\"accepted_suggestions\"],\n",
        "        \"monthly_cost\": monthly_cost,\n",
        "        \"efficiency\": efficiency,\n",
        "    })\n",
        "    print(\n",
        "        f'{item[\"team\"]}: cost=${monthly_cost}, '\n",
        "        f'efficiency={efficiency:.2f} accepted suggestions per dollar'\n",
        "    )\n",
        "\n",
        "df_eff = pd.DataFrame(rows).sort_values(\"efficiency\", ascending=False)\n",
        "df_eff"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Validation workflow: build a shared review dataset\n",
        "\n",
        "A core recommendation in the post is to create one shared dashboard or recurring report that engineering platform, finance, and managers can all read. This cell combines seat activity, budget context, and review actions into one normalized table."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "PRICE_PER_SEAT = 19\n",
        "LOW_USAGE_THRESHOLD = 5\n",
        "\n",
        "seat_export = pd.DataFrame([\n",
        "    {\"user\": \"ana\", \"team\": \"platform\", \"cost_center\": \"ENG-PLAT\", \"active_days\": 18, \"seat_type\": \"business\"},\n",
        "    {\"user\": \"ben\", \"team\": \"platform\", \"cost_center\": \"ENG-PLAT\", \"active_days\": 2, \"seat_type\": \"business\"},\n",
        "    {\"user\": \"chris\", \"team\": \"data\", \"cost_center\": \"ENG-DATA\", \"active_days\": 11, \"seat_type\": \"business\"},\n",
        "    {\"user\": \"dina\", \"team\": \"security\", \"cost_center\": \"ENG-SEC\", \"active_days\": 4, \"seat_type\": \"trial\"},\n",
        "    {\"user\": \"eli\", \"team\": \"security\", \"cost_center\": \"ENG-SEC\", \"active_days\": 13, \"seat_type\": \"business\"},\n",
        "])\n",
        "\n",
        "budget_context = pd.DataFrame([\n",
        "    {\"team\": \"platform\", \"budget_owner\": \"mgr_platform\", \"monthly_budget\": 200},\n",
        "    {\"team\": \"data\", \"budget_owner\": \"mgr_data\", \"monthly_budget\": 100},\n",
        "    {\"team\": \"security\", \"budget_owner\": \"mgr_security\", \"monthly_budget\": 60},\n",
        "])\n",
        "\n",
        "review_df = seat_export.merge(budget_context, on=\"team\", how=\"left\")\n",
        "review_df[\"monthly_seat_cost\"] = PRICE_PER_SEAT\n",
        "review_df[\"reclaim_candidate\"] = review_df[\"active_days\"] < LOW_USAGE_THRESHOLD\n",
        "review_df[\"review_action\"] = review_df[\"reclaim_candidate\"].map({True: \"manager_review\", False: \"keep\"})\n",
        "review_df"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Aggregate by team for monthly review\n",
        "\n",
        "This summarizes the normalized dataset into a manager-friendly monthly review. It shows seat counts, reclaim candidates, total cost, and budget variance so teams can decide whether to reclaim, renew, or expand."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "team_summary = (\n",
        "    review_df.groupby([\"team\", \"cost_center\", \"budget_owner\", \"monthly_budget\"], as_index=False)\n",
        "    .agg(\n",
        "        seats=(\"user\", \"count\"),\n",
        "        reclaim_candidates=(\"reclaim_candidate\", \"sum\"),\n",
        "        total_monthly_cost=(\"monthly_seat_cost\", \"sum\"),\n",
        "        avg_active_days=(\"active_days\", \"mean\"),\n",
        "    )\n",
        ")\n",
        "team_summary[\"budget_variance\"] = team_summary[\"monthly_budget\"] - team_summary[\"total_monthly_cost\"]\n",
        "team_summary.sort_values([\"reclaim_candidates\", \"budget_variance\"], ascending=[False, True])"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Visualize spend and reclaim candidates\n",
        "\n",
        "A lightweight chart helps make the control plane visible. This mirrors the blog's recommendation to avoid fragmented spreadsheets and instead publish one readable view for engineering and finance."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "fig, axes = plt.subplots(1, 2, figsize=(12, 4))\n",
        "\n",
        "axes[0].bar(team_summary[\"team\"], team_summary[\"total_monthly_cost\"], color=\"#4C78A8\")\n",
        "axes[0].set_title(\"Monthly Cost by Team\")\n",
        "axes[0].set_ylabel(\"Cost ($)\")\n",
        "\n",
        "axes[1].bar(team_summary[\"team\"], team_summary[\"reclaim_candidates\"], color=\"#F58518\")\n",
        "axes[1].set_title(\"Reclaim Candidates by Team\")\n",
        "axes[1].set_ylabel(\"Seats\")\n",
        "\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Simulate exception handling\n",
        "\n",
        "The blog stresses that defaults are not enough without a fast exception path. This example evaluates short-term exception requests using team, reason, duration, and owner, then marks whether the request fits a lightweight governance model."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "allowed_exception_reasons = {\n",
        "    \"large_migration\",\n",
        "    \"release_stabilization\",\n",
        "    \"incident_response\",\n",
        "    \"broad_refactoring\",\n",
        "    \"agent_assisted_codebase_work\",\n",
        "}\n",
        "\n",
        "exception_requests = pd.DataFrame([\n",
        "    {\"team\": \"platform\", \"reason\": \"large_migration\", \"duration_days\": 21, \"owner\": \"mgr_platform\"},\n",
        "    {\"team\": \"data\", \"reason\": \"release_stabilization\", \"duration_days\": 10, \"owner\": \"mgr_data\"},\n",
        "    {\"team\": \"security\", \"reason\": \"unknown\", \"duration_days\": 14, \"owner\": \"mgr_security\"},\n",
        "    {\"team\": \"sales\", \"reason\": \"incident_response\", \"duration_days\": 7, \"owner\": None},\n",
        "])\n",
        "\n",
        "approved_teams = {\"platform\", \"data\", \"security\"}\n",
        "\n",
        "def evaluate_exception(row):\n",
        "    if row[\"team\"] not in approved_teams:\n",
        "        return \"deny_unapproved_team\"\n",
        "    if row[\"reason\"] not in allowed_exception_reasons:\n",
        "        return \"needs_manual_review_reason\"\n",
        "    if not row[\"owner\"]:\n",
        "        return \"deny_missing_owner\"\n",
        "    if row[\"duration_days\"] > 30:\n",
        "        return \"needs_manual_review_duration\"\n",
        "    return \"approve_temporary\"\n",
        "\n",
        "exception_requests[\"decision\"] = exception_requests.apply(evaluate_exception, axis=1)\n",
        "exception_requests"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Generate a simple monthly action list\n",
        "\n",
        "This final operational example turns the review data into concrete actions. It reflects the blog's guidance that every overage, exception, or unusual pattern needs a named owner, a reason, and a next review date."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "action_rows = []\n",
        "review_date = (datetime.now() + timedelta(days=30)).date().isoformat()\n",
        "\n",
        "for _, row in review_df.iterrows():\n",
        "    if row[\"reclaim_candidate\"]:\n",
        "        reason = \"low_usage\"\n",
        "        action = \"review_for_reclaim\"\n",
        "    else:\n",
        "        reason = \"normal_usage\"\n",
        "        action = \"retain\"\n",
        "    action_rows.append({\n",
        "        \"user\": row[\"user\"],\n",
        "        \"team\": row[\"team\"],\n",
        "        \"owner\": row[\"budget_owner\"],\n",
        "        \"reason\": reason,\n",
        "        \"action\": action,\n",
        "        \"next_review_date\": review_date,\n",
        "    })\n",
        "\n",
        "action_df = pd.DataFrame(action_rows)\n",
        "action_df.sort_values([\"action\", \"team\", \"user\"])"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Summary\n",
        "\n",
        "This notebook validated the blog's main claim: Copilot spend is best managed as a governance system, not as a last-minute finance reaction. The practical controls are straightforward: visibility first, policy checks before provisioning, delegated approvals, temporary exceptions with owners, and recurring reclaim reviews.\n",
        "\n",
        "## Next Steps\n",
        "\n",
        "1. Replace the sample datasets with your real seat export and team-cost-center mapping.\n",
        "2. Add your default quota policy and manager approval rules.\n",
        "3. Define exception reasons, owners, and maximum durations.\n",
        "4. Publish a monthly dashboard for engineering managers, platform, and finance.\n",
        "5. Automate reclaim-or-renew decisions based on usage plus team context."
      ]
    }
  ]
}