feat(chart): real-world developer AI adoption and code quality
This commit is contained in:
222
src/charts/developer_reality.py
Normal file
222
src/charts/developer_reality.py
Normal file
@@ -0,0 +1,222 @@
|
||||
"""Real-World Developer AI Adoption and Code Quality Chart"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use("Agg")
|
||||
import matplotlib.pyplot as plt
|
||||
from src.data.agent_adoption import (
|
||||
developer_ai_adoption, code_acceptance_rates,
|
||||
code_quality_in_production, failure_modes
|
||||
)
|
||||
from src.utils.styling import (
|
||||
get_theme, EXPORT_DPI, AGENT_GROWTH, BUBBLE_ZONE,
|
||||
WARNING_ZONE, NORMAL_ZONE, GRAY_DARK, GRAY_LIGHT
|
||||
)
|
||||
|
||||
|
||||
def plot_developer_reality() -> str:
|
||||
plt.rcParams.update(get_theme())
|
||||
|
||||
fig = plt.figure(figsize=(16, 12))
|
||||
fig.set_facecolor("#ffffff")
|
||||
|
||||
# --- Layout: 3 panels via GridSpec ---
|
||||
from matplotlib.gridspec import GridSpec
|
||||
gs = GridSpec(2, 2, figure=fig,
|
||||
height_ratios=[1, 1.5],
|
||||
hspace=0.45, wspace=0.30,
|
||||
left=0.07, right=0.93, top=0.88, bottom=0.06)
|
||||
|
||||
ax1 = fig.add_subplot(gs[0, 0]) # Panel A — Adoption
|
||||
ax2 = fig.add_subplot(gs[0, 1]) # Panel B — Acceptance
|
||||
ax3 = fig.add_subplot(gs[1, :]) # Panel C — Code Quality (full width)
|
||||
|
||||
# =========================================================
|
||||
# Panel A: AI Coding Tool Adoption Rates
|
||||
# =========================================================
|
||||
adoption_labels = [
|
||||
"84% use or plan AI tools\n(Stack Overflow 2025)",
|
||||
"51% professional devs\nuse AI daily (Stack Overflow)",
|
||||
"85% regular AI usage\n(JetBrains 2025)",
|
||||
"62% rely on coding\nassistant (JetBrains)",
|
||||
"91% AI adoption in active\nrepos (DX DevCycle)",
|
||||
]
|
||||
adoption_values = [84, 51, 85, 62, 91]
|
||||
adoption_colors = [
|
||||
AGENT_GROWTH, AGENT_GROWTH,
|
||||
"#5b2d8e", "#3c1d6e",
|
||||
AGENT_GROWTH,
|
||||
]
|
||||
|
||||
bars_a = ax1.barh(
|
||||
range(len(adoption_labels)),
|
||||
adoption_values,
|
||||
color=adoption_colors,
|
||||
edgecolor="white",
|
||||
height=0.55,
|
||||
)
|
||||
ax1.set_yticks(range(len(adoption_labels)))
|
||||
ax1.set_yticklabels(adoption_labels, fontsize=9.5)
|
||||
ax1.set_xlim(0, 105)
|
||||
ax1.set_xticks(range(0, 110, 10))
|
||||
ax1.tick_params(axis="x", labelsize=8)
|
||||
ax1.invert_yaxis()
|
||||
|
||||
for bar, val in zip(bars_a, adoption_values):
|
||||
ax1.text(val + 1.5, bar.get_y() + bar.get_height() / 2,
|
||||
f"{val}%", va="center", fontsize=10,
|
||||
fontweight="bold", color=GRAY_DARK)
|
||||
|
||||
ax1.set_title("AI Coding Tool Adoption Rates",
|
||||
fontsize=14, fontweight="bold", pad=10)
|
||||
ax1.grid(True, alpha=0.3, axis="x")
|
||||
ax1.spines["top"].set_visible(False)
|
||||
ax1.spines["right"].set_visible(False)
|
||||
|
||||
# =========================================================
|
||||
# Panel B: Code Acceptance Rates
|
||||
# =========================================================
|
||||
acceptance_labels = [
|
||||
"~30% acceptance rate\n(GitHub Copilot)",
|
||||
"88% code retention rate\n(GitHub Copilot)",
|
||||
"22% of merged code is\nAI-authored (DX DevCycle)",
|
||||
"71% do NOT merge AI code\nwithout manual review",
|
||||
]
|
||||
acceptance_values = [30, 88, 22, 71]
|
||||
acceptance_colors = [
|
||||
WARNING_ZONE, # 30% acceptance — warning
|
||||
NORMAL_ZONE, # 88% retention — good
|
||||
AGENT_GROWTH, # 22% AI-authored — neutral
|
||||
GRAY_DARK, # 71% manual review — caution signal
|
||||
]
|
||||
|
||||
bars_b = ax2.barh(
|
||||
range(len(acceptance_labels)),
|
||||
acceptance_values,
|
||||
color=acceptance_colors,
|
||||
edgecolor="white",
|
||||
height=0.55,
|
||||
)
|
||||
ax2.set_yticks(range(len(acceptance_labels)))
|
||||
ax2.set_yticklabels(acceptance_labels, fontsize=9.5)
|
||||
ax2.set_xlim(0, 105)
|
||||
ax2.set_xticks(range(0, 110, 10))
|
||||
ax2.tick_params(axis="x", labelsize=8)
|
||||
ax2.invert_yaxis()
|
||||
|
||||
for bar, val in zip(bars_b, acceptance_values):
|
||||
ax2.text(val + 1.5, bar.get_y() + bar.get_height() / 2,
|
||||
f"{val}%", va="center", fontsize=10,
|
||||
fontweight="bold", color=GRAY_DARK)
|
||||
|
||||
ax2.set_title("Code Acceptance Rates",
|
||||
fontsize=14, fontweight="bold", pad=10)
|
||||
ax2.grid(True, alpha=0.3, axis="x")
|
||||
ax2.spines["top"].set_visible(False)
|
||||
ax2.spines["right"].set_visible(False)
|
||||
|
||||
# Annotation: adoption vs acceptance gap
|
||||
ax2.annotate(
|
||||
"HUGE GAP:\nHigh adoption,\nlow acceptance",
|
||||
xy=(30, 1.8), xytext=(58, 0.8),
|
||||
arrowprops=dict(arrowstyle="->", color=BUBBLE_ZONE, lw=2),
|
||||
fontsize=10, fontweight="bold", color=BUBBLE_ZONE,
|
||||
ha="center",
|
||||
bbox=dict(boxstyle="round,pad=0.3", facecolor=GRAY_LIGHT,
|
||||
edgecolor=BUBBLE_ZONE, linewidth=1.2),
|
||||
)
|
||||
|
||||
# =========================================================
|
||||
# Panel C: Code Quality in Production
|
||||
# =========================================================
|
||||
quality_labels = [
|
||||
"29.1% Python AI code has\nsecurity weaknesses",
|
||||
"24.2% JavaScript AI code has\nsecurity weaknesses",
|
||||
"48% AI-generated code has\npotential vulnerabilities",
|
||||
"1.7x more issues in\nAI-coauthored PRs (CodeRabbit)",
|
||||
"7.2% drop in delivery\nstability (Google DORA)",
|
||||
]
|
||||
quality_values = [29.1, 24.2, 48, 1.7, 7.2]
|
||||
# All bars use BUBBLE_ZONE to signal danger
|
||||
quality_colors = [BUBBLE_ZONE] * len(quality_labels)
|
||||
|
||||
bars_c = ax3.barh(
|
||||
range(len(quality_labels)),
|
||||
quality_values,
|
||||
color=quality_colors,
|
||||
edgecolor="white",
|
||||
height=0.45,
|
||||
)
|
||||
ax3.set_yticks(range(len(quality_labels)))
|
||||
ax3.set_yticklabels(quality_labels, fontsize=10)
|
||||
|
||||
# X-axis scaled to the max value
|
||||
ax3.set_xlim(0, max(quality_values) * 1.25)
|
||||
ax3.set_xticks([0, 10, 20, 30, 40, 50])
|
||||
ax3.tick_params(axis="x", labelsize=9)
|
||||
ax3.invert_yaxis()
|
||||
|
||||
for bar, val in zip(bars_c, quality_values):
|
||||
label = f"{val}x" if val < 5 and val != int(val) else f"{val}"
|
||||
ax3.text(val + 1, bar.get_y() + bar.get_height() / 2,
|
||||
label, va="center", fontsize=11,
|
||||
fontweight="bold", color="#c0392b")
|
||||
|
||||
ax3.set_title("Code Quality Concerns in Production",
|
||||
fontsize=14, fontweight="bold", pad=10,
|
||||
color=BUBBLE_ZONE)
|
||||
ax3.grid(True, alpha=0.3, axis="x")
|
||||
ax3.spines["top"].set_visible(False)
|
||||
ax3.spines["right"].set_visible(False)
|
||||
|
||||
# =========================================================
|
||||
# Figure-level title and disclaimer
|
||||
# =========================================================
|
||||
fig.suptitle(
|
||||
"Real-World Developer AI: Adoption vs. Code Quality",
|
||||
fontsize=18, fontweight="bold", y=0.96,
|
||||
color=GRAY_DARK,
|
||||
)
|
||||
fig.text(
|
||||
0.5, 0.925,
|
||||
"Benchmarks measure lab tasks, not production shipping",
|
||||
ha="center", fontsize=13, style="italic",
|
||||
color=GRAY_DARK, alpha=0.8,
|
||||
)
|
||||
|
||||
# Prominent disclaimer banner
|
||||
fig.text(
|
||||
0.5, 0.015,
|
||||
"⚠ Benchmarks measure controlled lab tasks, NOT production shipping",
|
||||
ha="center", fontsize=12, fontweight="bold",
|
||||
color=BUBBLE_ZONE,
|
||||
bbox=dict(
|
||||
boxstyle="round,pad=0.5",
|
||||
facecolor=GRAY_LIGHT,
|
||||
edgecolor=BUBBLE_ZONE,
|
||||
linewidth=2,
|
||||
),
|
||||
)
|
||||
|
||||
# =========================================================
|
||||
# Save
|
||||
# =========================================================
|
||||
out_path = "output/charts/12_developer_ai_reality.png"
|
||||
fig.savefig(
|
||||
out_path, dpi=EXPORT_DPI,
|
||||
facecolor=fig.get_facecolor(),
|
||||
edgecolor="none",
|
||||
)
|
||||
plt.close(fig)
|
||||
return out_path
|
||||
|
||||
|
||||
def main():
|
||||
path = plot_developer_reality()
|
||||
print(f"Chart saved: {path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user