Skip to content
Santekno.com | Level Up Your Engineering Skills
EN
📖 0%
02 Oct 2026 · 22 min read ·Article 58 / 208
Go

AI in the Go CI/CD Pipeline: Quality Gates, Auto Review, Architecture Check

Integrate AI tools into the Golang CI/CD pipeline. Pre-commit AI review, GitHub Actions quality gates, architecture enforcement, and AI-generated PR descriptions for Go.

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

AI Tools in the Golang CI/CD Pipeline

Integrating AI tools into the Golang CI/CD pipeline unlocks a class of quality gate that ordinary static analysis cannot reach. AI tools are useful not only while a developer is coding — but also in the pipeline for smarter checks. This article shows how to integrate AI into a Go pipeline in a practical, cost-effective way, from the pre-commit hook to the release notes.


18.1 CI/CD Stages That Benefit from AI

Not every stage of the pipeline needs AI. The map below marks the five stages that most clearly gain value from AI along with their estimated cost.

text
 1Pipeline stages where AI provides clear value:
 2
 3Stage 1: Pre-commit (local, before push)
 4  - Quick AI review via a cheap model (haiku)
 5  - Architecture violation check
 6  - Secret detection
 7  Cost: < $0.001 per commit
 8
 9Stage 2: Pull Request (GitHub Actions)
10  - Copilot automated PR review (built-in if you have Copilot)
11  - Spec compliance audit
12  - AI-generated PR description
13  Cost: $0.001-0.01 per PR
14
15Stage 3: Build & Test
16  - Test failure AI analysis (only if failure)
17  - Coverage gap identification
18  Cost: $0 if it passes, minimal if it fails
19
20Stage 4: Code Quality Gates
21  - Architecture enforcement (Go script, no AI cost)
22  - Complexity analysis
23  Cost: $0 (static analysis)
24
25Stage 5: Post-merge / Deployment
26  - AI-generated release notes
27  - Deployment notes generator
28  Cost: < $0.01 per deployment

This map reinforces the cost principle: AI is used only at points that add judgment (review, failure analysis), while mechanical enforcement is still handled by free static analysis.


18.2 Pre-commit Hook with AI Review

The cheapest intervention point is before the code is even pushed. The hook below runs a quick review with the haiku model against the staged diff and blocks the commit if there’s a CRITICAL issue.

bash
 1#!/bin/bash
 2# .git/hooks/pre-commit
 3# Quick AI review before commit
 4
 5set -e
 6
 7# Only check Go files
 8CHANGED_GO=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$' || true)
 9
10if [ -z "$CHANGED_GO" ]; then
11    exit 0  # No Go changes, skip
12fi
13
14echo "Running pre-commit AI review..."
15
16# Collect the diff from all changed files
17DIFF=$(echo "$CHANGED_GO" | while read -r file; do
18    git diff --cached "$file"
19done)
20
21if [ -z "$DIFF" ]; then
22    exit 0
23fi
24
25# Call the Claude API (haiku = cheap and fast)
26REVIEW=$(curl -sf -X POST https://api.anthropic.com/v1/messages \
27    -H "x-api-key: ${ANTHROPIC_API_KEY}" \
28    -H "anthropic-version: 2023-06-01" \
29    -H "content-type: application/json" \
30    -d "$(jq -n \
31        --arg diff "$DIFF" \
32        '{
33            model: "claude-haiku-4-5-20251001",
34            max_tokens: 300,
35            messages: [{
36                role: "user",
37                content: ("Go code quick review. ONLY flag CRITICAL:\n1. Error not wrapped (return err without fmt.Errorf)\n2. float64 for money (must int64)\n3. _ ignoring errors\n4. Architecture violation (handler import repo impl)\nDiff:\n" + $diff + "\nOutput: CRITICAL:[file:line issue] or OK")
38            }]
39        }'
40    )" | jq -r '.content[0].text' 2>/dev/null || echo "OK")
41
42if echo "$REVIEW" | grep -q "^CRITICAL:"; then
43    echo ""
44    echo "AI Pre-commit Review — CRITICAL ISSUES FOUND:"
45    echo "$REVIEW"
46    echo ""
47    echo "Fix the issues above before committing."
48    echo "Skip (not recommended): git commit --no-verify"
49    exit 1
50fi
51
52echo "AI Pre-commit: OK"
53exit 0

This hook gives the developer feedback before pushing, when fixing is still cheapest — only CRITICAL issues are blocked so as not to disrupt the daily workflow. To install it cleanly, use a pre-commit framework as shown below.

bash
 1# Install the hook:
 2chmod +x .git/hooks/pre-commit
 3
 4# Alternative: use the pre-commit framework
 5# .pre-commit-config.yaml
 6repos:
 7  - repo: local
 8    hooks:
 9      - id: ai-review
10        name: AI Quick Review
11        entry: .git/hooks/pre-commit
12        language: script
13        types: [go]

By registering the hook in .pre-commit-config.yaml, the whole team gets the same gate consistently instead of relying on each developer installing the hook manually.


18.3 A Complete GitHub Actions Workflow

After the pre-commit, the next layer is the GitHub Actions pipeline. The workflow below arranges five jobs — from the standard build/test to AI review and failure analysis — with AI used only where it’s appropriate.

yaml
  1# .github/workflows/ci.yml
  2
  3name: CI Pipeline with AI Quality Gates
  4
  5on:
  6  push:
  7    branches: [main, develop]
  8  pull_request:
  9    branches: [main, develop]
 10    types: [opened, synchronize, ready_for_review]
 11
 12env:
 13  GO_VERSION: '1.22'
 14
 15jobs:
 16  # Job 1: Standard Go (always run, no AI, fast)
 17  go-build-test:
 18    name: Build & Test
 19    runs-on: ubuntu-latest
 20    steps:
 21      - uses: actions/checkout@v4
 22      - uses: actions/setup-go@v5
 23        with:
 24          go-version: ${{ env.GO_VERSION }}
 25          cache: true
 26
 27      - name: Build
 28        run: go build ./...
 29
 30      - name: Test with Race
 31        run: go test -race -count=1 -timeout=5m ./...
 32
 33      - name: Coverage Gate
 34        run: |
 35          go test -coverprofile=coverage.out ./...
 36          COVERAGE=$(go tool cover -func=coverage.out | \
 37            grep "total:" | awk '{print $3}' | tr -d '%')
 38          echo "Test coverage: ${COVERAGE}%"
 39          if awk "BEGIN {exit ($COVERAGE >= 70) ? 0 : 1}"; then
 40            echo "Coverage ${COVERAGE}% >= 70%"
 41          else
 42            echo "Coverage ${COVERAGE}% < 70% threshold"
 43            exit 1
 44          fi
 45
 46      - name: Vet
 47        run: go vet ./...
 48
 49      - name: golangci-lint
 50        uses: golangci/golangci-lint-action@v6
 51        with:
 52          version: latest
 53
 54  # Job 2: Architecture Enforcement (no AI, custom Go script)
 55  architecture-check:
 56    name: Architecture Boundary Check
 57    runs-on: ubuntu-latest
 58    steps:
 59      - uses: actions/checkout@v4
 60      - uses: actions/setup-go@v5
 61        with:
 62          go-version: ${{ env.GO_VERSION }}
 63
 64      - name: Check layer boundaries
 65        run: go run ./scripts/check-architecture.go ./...
 66
 67  # Job 3: AI PR Review (only for non-draft PRs)
 68  ai-pr-review:
 69    name: Copilot PR Review
 70    runs-on: ubuntu-latest
 71    if: |
 72      github.event_name == 'pull_request' &&
 73      !github.event.pull_request.draft
 74    permissions:
 75      pull-requests: write
 76      contents: read
 77    steps:
 78      - uses: actions/checkout@v4
 79        with:
 80          fetch-depth: 0
 81      - uses: github/copilot-for-pull-requests@v1
 82        with:
 83          github-token: ${{ secrets.GITHUB_TOKEN }}
 84          review-instructions: |
 85            Review Go code. Flag as CRITICAL:
 86            - Error not wrapped: return err (must fmt.Errorf)
 87            - Repository returns error for not-found (must nil, nil)
 88            - float64 for monetary values
 89            - Architecture violations (wrong layer imports)
 90            - Errors ignored with _
 91            End with REVIEW_SCORE: X/100
 92
 93  # Job 4: AI-generated PR description (only for new PRs)
 94  generate-pr-description:
 95    name: Generate PR Description
 96    runs-on: ubuntu-latest
 97    if: github.event.action == 'opened'
 98    permissions:
 99      pull-requests: write
100      contents: read
101    env:
102      ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
103    steps:
104      - uses: actions/checkout@v4
105        with:
106          fetch-depth: 0
107      - uses: actions/setup-node@v4
108        with:
109          node-version: '20'
110      - name: Generate and update PR description
111        uses: actions/github-script@v7
112        with:
113          script: |
114            const { execSync } = require('child_process');
115            const diff = execSync(
116              'git diff origin/main...HEAD -- "*.go" 2>/dev/null | head -300',
117              { encoding: 'utf-8' }
118            );
119
120            if (!diff.trim()) {
121              console.log('No Go changes detected');
122              return;
123            }
124
125            const res = await fetch('https://api.anthropic.com/v1/messages', {
126              method: 'POST',
127              headers: {
128                'x-api-key': process.env.ANTHROPIC_API_KEY,
129                'anthropic-version': '2023-06-01',
130                'content-type': 'application/json',
131              },
132              body: JSON.stringify({
133                model: 'claude-haiku-4-5-20251001',
134                max_tokens: 600,
135                messages: [{
136                  role: 'user',
137                  content: `Generate a PR description from this Go diff.
138Format:
139## Summary
140[one paragraph on what this PR does]
141
142## Changes
143- [bullet list of key changes]
144
145## Testing
146- [ ] go test -race ./...
147
148Diff:
149${diff}`
150                }]
151              })
152            });
153
154            const data = await res.json();
155            const body = data.content?.[0]?.text || '';
156
157            await github.rest.pulls.update({
158              owner: context.repo.owner,
159              repo: context.repo.repo,
160              pull_number: context.issue.number,
161              body
162            });
163
164  # Job 5: Test failure analysis (only on failure)
165  analyze-test-failure:
166    name: Analyze Test Failure
167    runs-on: ubuntu-latest
168    needs: go-build-test
169    if: failure() && github.event_name == 'pull_request'
170    permissions:
171      pull-requests: write
172    env:
173      ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
174    steps:
175      - uses: actions/checkout@v4
176      - uses: actions/setup-go@v5
177        with:
178          go-version: ${{ env.GO_VERSION }}
179
180      - name: Capture test output
181        run: |
182          go test ./... 2>&1 | head -100 > /tmp/test-output.txt || true
183
184      - name: AI analysis of failure
185        run: |
186          OUTPUT=$(cat /tmp/test-output.txt)
187          ANALYSIS=$(curl -sf -X POST https://api.anthropic.com/v1/messages \
188            -H "x-api-key: ${ANTHROPIC_API_KEY}" \
189            -H "anthropic-version: 2023-06-01" \
190            -H "content-type: application/json" \
191            -d "$(jq -n --arg out "$OUTPUT" '{
192              model: "claude-haiku-4-5-20251001",
193              max_tokens: 300,
194              messages: [{
195                role: "user",
196                content: ("Analyze this Go test failure briefly. Root cause and fix suggestion:\n" + $out)
197              }]
198            }')" | jq -r '.content[0].text' || echo "Analysis failed")
199
200          gh pr comment ${{ github.event.number }} \
201            --body "## AI Test Failure Analysis\n\n${ANALYSIS}"
202        env:
203          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

The design of this pipeline matters: the build/test/architecture jobs run without AI and stay fast, while the AI jobs (review, PR description, failure analysis) are conditioned to run only when relevant — so cost and time stay under control.


18.4 Architecture Enforcement Script

The architecture-check job above calls a Go script with no AI. The script below parses each file’s imports and fails the build if a layer violates the Clean Architecture rules.

go
 1// scripts/check-architecture.go
 2// Enforce Clean Architecture layer boundaries
 3
 4//go:build ignore
 5
 6package main
 7
 8import (
 9    "fmt"
10    "go/parser"
11    "go/token"
12    "os"
13    "path/filepath"
14    "strings"
15)
16
17// forbiddenImports: layer prefix -> list of forbidden import substrings
18var forbiddenImports = map[string][]string{
19    "internal/delivery": {
20        "internal/repository", // handler must NOT import repo impl
21        "internal/usecase/",   // handler imports usecase INTERFACE, not pkg directly
22    },
23    "internal/usecase": {
24        "internal/delivery",         // usecase must NOT import handler
25        "github.com/jackc/pgx",      // usecase must NOT import DB driver
26        "github.com/redis/go-redis", // usecase must NOT import Redis
27        "github.com/labstack/echo",  // usecase must NOT import HTTP framework
28    },
29    "internal/domain": {
30        "github.com/jackc/pgx",
31        "github.com/redis/go-redis",
32        "github.com/labstack/echo",
33        "github.com/confluentinc/confluent-kafka-go",
34    },
35}
36
37func main() {
38    violations := 0
39    fset := token.NewFileSet()
40
41    err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
42        if err != nil || info.IsDir() {
43            return err
44        }
45        if !strings.HasSuffix(path, ".go") {
46            return nil
47        }
48        if strings.HasSuffix(path, "_test.go") {
49            return nil // skip tests
50        }
51
52        // Determine which layer this file is in
53        var currentLayer string
54        for layer := range forbiddenImports {
55            if strings.Contains(path, layer) {
56                currentLayer = layer
57                break
58            }
59        }
60        if currentLayer == "" {
61            return nil
62        }
63
64        f, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly)
65        if err != nil {
66            return nil
67        }
68
69        for _, imp := range f.Imports {
70            impPath := strings.Trim(imp.Path.Value, `"`)
71            for _, forbidden := range forbiddenImports[currentLayer] {
72                if strings.Contains(impPath, forbidden) {
73                    fmt.Printf("VIOLATION [%s]: %s\n  imports: %s\n\n",
74                        currentLayer, path, impPath)
75                    violations++
76                }
77            }
78        }
79        return nil
80    })
81
82    if err != nil {
83        fmt.Fprintf(os.Stderr, "walk error: %v\n", err)
84        os.Exit(2)
85    }
86
87    if violations > 0 {
88        fmt.Printf("Total: %d architecture violations\n", violations)
89        os.Exit(1)
90    }
91
92    fmt.Println("Architecture check: PASSED")
93}

This script is the highest-value-per-dollar gate: zero AI cost, deterministic, and it catches architecture violations — the kind of bug that’s most expensive if it slips into production. Run it unconditionally on every PR.


18.5 AI-Generated Release Notes

AI is also useful in the post-merge stage, for example composing a changelog. The Go program below gathers commits since the last tag and asks the haiku model to summarize them into clean release notes.

go
 1// cmd/release-notes/main.go
 2// Run before each release to generate the changelog
 3
 4package main
 5
 6import (
 7    "context"
 8    "fmt"
 9    "os"
10    "os/exec"
11    "strings"
12
13    anthropic "github.com/anthropics/anthropic-sdk-go"
14)
15
16func main() {
17    // Get the commit log from the last tag
18    lastTag, _ := exec.Command("git", "describe", "--tags", "--abbrev=0").Output()
19    tag := strings.TrimSpace(string(lastTag))
20
21    commits, err := exec.Command(
22        "git", "log", "--oneline", "--no-merges",
23        fmt.Sprintf("%s..HEAD", tag),
24    ).Output()
25    if err != nil {
26        fmt.Fprintln(os.Stderr, "No previous tag found, using all commits")
27        commits, _ = exec.Command("git", "log", "--oneline", "--no-merges", "-50").Output()
28    }
29
30    if len(commits) == 0 {
31        fmt.Println("No commits found")
32        return
33    }
34
35    client := anthropic.NewClient()
36
37    msg, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
38        Model:     anthropic.F(anthropic.ModelClaudeHaiku4_5),
39        MaxTokens: anthropic.F(int64(800)),
40        Messages: anthropic.F([]anthropic.MessageParam{
41            anthropic.NewUserMessage(anthropic.NewTextBlock(fmt.Sprintf(`
42Generate release notes from the following commit messages for a Go service.
43Clean markdown format:
44
45## What's New
46[new features]
47
48## Bug Fixes
49[bug fixes]
50
51## Internal Changes
52[refactoring, dependencies, etc.]
53
54Commits (from %s to HEAD):
55%s`, tag, string(commits)))),
56        }),
57    })
58    if err != nil {
59        fmt.Fprintf(os.Stderr, "Claude error: %v\n", err)
60        os.Exit(1)
61    }
62
63    fmt.Println(msg.Content[0].Text)
64}

This program turns the tedious task of writing a changelog into a single command — even non-descriptive commits still get summarized neatly because the AI groups them by category.


18.6 Cost Estimation for AI in CI/CD

The biggest fear of adopting AI in CI is usually cost. The breakdown below computes a realistic monthly estimate for a 5-developer team and shows that the API calls are not the main cost.

text
 1Monthly cost estimate for a 5-developer team, 3 PRs/dev/week:
 2
 3Pre-commit hook (haiku):
 4  Commits per month: 5 devs × 20 commits = 100
 5  Input: ~500 tokens, output: ~100 tokens
 6  Cost per commit: $0.0001
 7  Monthly: 100 × $0.0001 = $0.01
 8
 9PR description generation (haiku):
10  PRs per month: 5 × 3 × 4 weeks = 60 PRs
11  Input: ~1000 tokens, output: ~400 tokens
12  Cost per PR: $0.0002
13  Monthly: 60 × $0.0002 = $0.012
14
15Test failure analysis (haiku):
16  Failures per month: ~10 (assuming an 83% PR pass rate)
17  Input: ~500 tokens, output: ~300 tokens
18  Cost per failure: $0.0001
19  Monthly: 10 × $0.0001 = $0.001
20
21Release notes (haiku):
22  Releases per month: 4
23  Cost: 4 × $0.001 = $0.004
24
25Total AI API cost: ~$0.03/month
26GitHub Copilot Business (PR review): $19 × 5 = $95/month
27
28Copilot is the main cost, not the API calls.
29API calls for automation: very affordable.

The conclusion from these numbers is reassuring: haiku-based automation only consumes about $0.03/month — the real cost is the Copilot subscription, not the API calls you add yourself.


18.7 Security: Redact Secrets Before AI Review

Sending a diff to an AI API risks leaking a secret. The script below filters common secret patterns from the diff before sending it to the model.

bash
 1#!/bin/bash
 2# scripts/redact-diff.sh
 3# Redact potential secrets from the diff before sending to AI
 4
 5DIFF="$1"
 6
 7# Common secret patterns
 8echo "$DIFF" | \
 9  sed 's/sk-ant-[a-zA-Z0-9_-]*/sk-ant-REDACTED/g' | \
10  sed 's/ghp_[a-zA-Z0-9]*/ghp_REDACTED/g' | \
11  sed 's/AKIA[A-Z0-9]*/AKIAXXXXXXXXXXXXXXXX/g' | \
12  sed 's/password=[^\s]*/password=REDACTED/g' | \
13  sed 's/secret=[^\s]*/secret=REDACTED/g' | \
14  sed 's/DATABASE_URL=postgres:\/\/[^\s]*/DATABASE_URL=postgres:\/\/REDACTED/g'

This redaction is an important line of defense: even though an AI provider has a retention policy, preventing secrets from leaving your machine in the first place is far safer. How to use it inside the pipeline is shown below.

yaml
 1# Use in GitHub Actions:
 2- name: Redact secrets from diff
 3  run: |
 4    git diff origin/main...HEAD -- '*.go' | \
 5      bash scripts/redact-diff.sh > /tmp/safe-diff.txt
 6
 7- name: AI Review on safe diff
 8  run: |
 9    # Use /tmp/safe-diff.txt instead of the raw diff
10    # ... call the AI API with safe-diff.txt

By placing the redaction step before every AI call, you ensure that what’s sent to the API is always the cleaned version — not the raw diff that might carry credentials.


18.8 Monitoring AI Usage in CI

AI cost needs to be monitored so it doesn’t quietly balloon. The step below logs AI usage metrics to the GitHub Step Summary for cost control.

yaml
 1# Track AI spending in CI for cost control
 2- name: Track AI Usage
 3  if: always()
 4  run: |
 5    # Log basic metrics to the GitHub Step Summary
 6    cat >> $GITHUB_STEP_SUMMARY << EOF
 7    ## AI Usage This Run
 8    - Model: claude-haiku-4-5-20251001
 9    - Task: PR description generation
10    - Estimated cost: <$0.001
11    EOF
12
13# Monthly cost report via a scheduled job:
14- name: Monthly AI Cost Report
15  if: github.event_name == 'schedule'
16  run: |
17    # Aggregate from the billing API or manual tracking
18    echo "Review AI tool costs at: anthropic.com/account/usage"

Logging the cost estimate per run directly in the workflow summary makes AI spending transparent to the whole team — a billing surprise at the end of the month can be avoided.


18.9 Graduated AI Enforcement

Forcing a full AI gate from day one often triggers resistance. The graduated enforcement strategy below introduces the rules gradually, from a mere warning up to a full score gate.

text
 1A gentler pipeline enforcement strategy for adoption:
 2
 3PHASE 1 (Month 1): Warning only
 4  - The AI review runs but does NOT block merge
 5  - The developer sees the output but isn't forced to fix
 6  - Collect a baseline: how many issues per PR?
 7
 8PHASE 2 (Month 2): Enforce CRITICAL only
 9  - CRITICAL issues block merge
10  - SUGGESTION remains a warning
11  - Target: zero CRITICAL in merge
12
13PHASE 3 (Month 3+): Full enforcement
14  - CRITICAL blocks merge
15  - SUGGESTION blocks merge if > 5 per PR
16  - Score gate: AI review score < 80 = block
17
18Config for graduated enforcement:
19  .github/ai-enforcement.yml:
20    phase: 2
21    block_on_critical: true
22    block_on_suggestions: false
23    minimum_score: 0  # not enforced yet

This gradual approach keeps adoption humane: the team has time to adjust and build trust in the AI review before the gate starts genuinely blocking merges.


18.10 Tips & Gotchas

The practical lessons below summarize what usually determines the success or failure of AI integration in CI/CD.

Tip 1: Start with just the pre-commit hook — zero GitHub Actions setup, immediate value, and the developer gets feedback before even pushing.

Tip 2: Use the haiku model for CI tasks — 10× cheaper than Sonnet, sufficient for mechanical review in CI. Reserve Sonnet for complex reasoning in developer sessions.

Tip 3: Cache AI results by commit hash — if a PR has no new commits since the last review, skip the AI call to save cost.

Tip 4: The architecture check script (18.4) does NOT need AI — pure static analysis. Run this before the AI steps for a quick catch without cost.

Gotcha 1: The ANTHROPIC_API_KEY in GitHub Secrets must be rotated at least every 6 months. Set up the key with minimal permissions.

Gotcha 2: AI calls add to CI time. Design them as parallel jobs, not sequential. PR description and architecture check can run in parallel.

Gotcha 3: Don’t send the entire codebase to the AI in CI. Only the relevant diff. Sending more = more expensive + slower + possibly exposing unneeded code.

The common thread is clear: use a cheap model for mechanical tasks, run AI jobs in parallel, and limit the input to only the relevant diff — three habits that keep the pipeline fast and cheap.


18.11 Integration with Existing Go Quality Tools

AI is not a replacement for the Go quality tools you already have; it’s an additional layer. The workflow below arranges all the layers — build, test, vet, lint, architecture, then AI — in a logical order.

yaml
 1# Full pipeline that combines all the tools:
 2
 3name: Complete Quality Pipeline
 4
 5on: [pull_request]
 6
 7jobs:
 8  quality:
 9    runs-on: ubuntu-latest
10    steps:
11      # Layer 1: Standard Go (fastest, no cost)
12      - name: Build
13        run: go build ./...
14
15      - name: Test
16        run: go test -race ./...
17
18      - name: Vet
19        run: go vet ./...
20
21      # Layer 2: golangci-lint (no AI, great coverage)
22      - uses: golangci/golangci-lint-action@v6
23
24      # Layer 3: Architecture check (no AI, custom rules)
25      - name: Architecture
26        run: go run ./scripts/check-architecture.go ./...
27
28      # Layer 4: AI review (adds judgment, has cost)
29      - name: AI Review
30        if: '!github.event.pull_request.draft'
31        uses: github/copilot-for-pull-requests@v1
32        with:
33          github-token: ${{ secrets.GITHUB_TOKEN }}
34
35# Each layer catches different issues:
36# Build: compilation errors
37# Test: runtime behavior
38# Vet: suspicious constructs
39# golangci-lint: style + common mistakes
40# Architecture: layer boundary violations
41# AI: semantic issues, missing error handling, Go idiom

This layered order is cost-efficient: the free and fast layers (build, test, lint, architecture) filter out the majority of problems first, so the AI only evaluates code that has already passed the cheap gates.


18.12 A Workflow for Database Migration Review

SQL migrations are a high-risk area well suited to a dedicated AI review. The script below asks the AI to flag dangerous migrations (DROP, TRUNCATE, heavy ALTER) before they’re applied.

bash
 1#!/bin/bash
 2# scripts/review-migration.sh
 3# AI review for SQL migration files
 4
 5MIGRATION_FILE="$1"
 6
 7if [ -z "$MIGRATION_FILE" ]; then
 8    echo "Usage: review-migration.sh <migration-file>"
 9    exit 1
10fi
11
12SQL=$(cat "$MIGRATION_FILE")
13
14REVIEW=$(curl -sf -X POST https://api.anthropic.com/v1/messages \
15    -H "x-api-key: ${ANTHROPIC_API_KEY}" \
16    -H "anthropic-version: 2023-06-01" \
17    -H "content-type: application/json" \
18    -d "$(jq -n \
19        --arg sql "$SQL" \
20        '{
21            model: "claude-haiku-4-5-20251001",
22            max_tokens: 400,
23            messages: [{
24                role: "user",
25                content: ("Review SQL migration. Flag:\nBLOCKING: DROP without backup, TRUNCATE, lock-heavy ALTER on large table\nWARNING: Missing index for FK, NOT NULL without default on existing table\nOK: safe migration\n\nMigration:\n" + $sql)
26            }]
27        }'
28    )" | jq -r '.content[0].text')
29
30echo "$REVIEW"
31
32# Block if there are BLOCKING issues
33if echo "$REVIEW" | grep -q "^BLOCKING:"; then
34    exit 1
35fi

Migration review is one of the highest-ROI uses of AI in CI: a single migration that locks a large table can cause production downtime, and this gate catches it before merge.


18.13 Slack/Discord Notification with an AI Summary

After a deployment, teams usually want a short, easy-to-read summary. The step below asks the AI to summarize the commits into 2-3 sentences and send them to Slack.

yaml
 1# Send an AI summary to Slack after deployment
 2
 3- name: Generate Deployment Summary
 4  if: github.ref == 'refs/heads/main' && success()
 5  env:
 6    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
 7    SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
 8  run: |
 9    COMMITS=$(git log --oneline --no-merges -10)
10
11    SUMMARY=$(curl -sf -X POST https://api.anthropic.com/v1/messages \
12        -H "x-api-key: ${ANTHROPIC_API_KEY}" \
13        -H "anthropic-version: 2023-06-01" \
14        -H "content-type: application/json" \
15        -d "$(jq -n --arg c "$COMMITS" '{
16          model: "claude-haiku-4-5-20251001",
17          max_tokens: 200,
18          messages: [{
19            role: "user",
20            content: ("Summarize these commits into 2-3 sentences for a Slack deployment notification. Be concise and informative:\n" + $c)
21          }]
22        }')" | jq -r '.content[0].text')
23
24    # Send to Slack
25    curl -X POST "$SLACK_WEBHOOK" \
26        -H "Content-Type: application/json" \
27        -d "$(jq -n \
28            --arg summary "$SUMMARY" \
29            --arg sha "${{ github.sha }}" \
30            '{
31              text: ("*Deployed to Production* :rocket:\n" + $summary + "\nCommit: " + $sha[:8])
32            }'
33        )"

A notification like this turns a dry list of commits into a summary even a non-engineer can understand — increasing deployment visibility with no manual effort.


18.14 Metrics Tracking for the AI CI Pipeline

To be able to evaluate the effectiveness of the AI review, its results need to be recorded as metrics. The step below saves the review score and critical status to an artifact for monthly trend analysis.

yaml
 1# Track the effectiveness of AI in CI with simple metrics
 2
 3# In GitHub Actions, save to GitHub Variables:
 4- name: Track AI Review Effectiveness
 5  uses: actions/github-script@v7
 6  with:
 7    script: |
 8      // Get the AI review result from the previous step
 9      const aiScore = parseInt(process.env.AI_SCORE || '0');
10      const hasCritical = process.env.HAS_CRITICAL === 'true';
11
12      // Log to the PR comment for tracking
13      const body = [
14        `## AI Quality Metrics`,
15        `- Score: ${aiScore}/100`,
16        `- Critical issues: ${hasCritical ? 'YES' : 'None'}`,
17        `- Timestamp: ${new Date().toISOString()}`,
18      ].join('\n');
19
20      // Store as a workflow artifact for monthly analysis
21      require('fs').writeFileSync(
22        `ai-metrics-${context.payload.number}.json`,
23        JSON.stringify({ pr: context.payload.number, score: aiScore, hasCritical, timestamp: new Date() })
24      );
25
26# Monthly: aggregate the metrics JSON files for trend analysis
27# "Average AI review score trending up = context files improving"

These metrics close the feedback loop: a review score that rises month over month is quantitative proof that the team’s context files and code quality are improving.


18.15 Troubleshooting Common CI/AI Issues

AI integration in CI has its own characteristic problems. The guide below maps the most frequent problems along with their practical solutions.

text
 1Problem: The pre-commit hook is too slow (> 30 seconds)
 2Solution:
 3  1. Use haiku (not sonnet) — 3× faster
 4  2. Limit the diff size: head -200 before sending to AI
 5  3. Only check CRITICAL rules (3-4 rules, not 10+)
 6  4. Cache the result: if the file didn't change, skip
 7
 8Problem: The AI review score varies for the same code
 9Solution:
10  1. A structured prompt with exact rules
11  2. A specific format request ("CRITICAL: or OK only")
12  3. Use temperature=0 if the API supports it
13
14Problem: Annoying false positives
15Solution:
16  1. Add exception rules to the prompt
17     "Exception: test files may use _ for errors"
18     "Exception: main.go may use log.Fatal"
19  2. Update CLAUDE.md for explicit exceptions
20  3. Track what kind of false positives, update the rules
21
22Problem: The AI review doesn't catch issues that slip through
23Solution:
24  1. Add a rule specific to that issue
25  2. Include a code example in the review prompt
26  3. Test the prompt with code that explicitly violates the rule

The solution pattern is consistent: a structured, specific prompt, limited input, plus a continually updated list of exceptions — a combination that makes AI review stable and trustworthy.


18.16 Summary

AI in CI/CD is the last layer of the quality-gate stack. It’s not a replacement for tests or lint — but an addition that catches patterns static analysis cannot.

Recommended setup per phase:

  • Week 1: Pre-commit hook (local, cheap, immediate)
  • Week 2: Copilot PR review (if you already have Copilot)
  • Week 3: Architecture check script (zero AI cost)
  • Month 2: Test failure analysis, PR description generation

The total setup for all of this is 1-2 days, with an ongoing cost of < $1/month for API calls — the value produced is a faster review cycle and human reviewers who can focus on logic, not mechanical issues. Combined with the context files and multi-tool workflow from the previous articles, this completes the AI-driven Go development ecosystem. The next step is to make sure all of this automation is secure: the security and privacy of AI coding tools.

Related Articles

💬 Comments