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

Architecture of an AI-Assisted CI/CD Pipeline for Golang: A Complete Blueprint

An architecture blueprint for an AI-assisted pipeline for a Go service. Layer-by-layer design: syntactic, semantic, AI review, spec compliance — with a production-ready GitHub Actions workflow.

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

Before writing a single line of pipeline YAML, we need a clear blueprint for the AI golang CI/CD architecture we’re about to build. A pipeline built without a well-thought-out architecture ends up as a collection of steps that are hard to maintain, too slow, or full of false positives.

This article is an architecture session — we’ll design the pipeline top to bottom before implementation.


02.1 Our Pipeline Design Principles

Before getting to the diagram, there are three principles that will guide every architecture decision in Topic 4. These three principles — fail fast, parallel, and graduated enforcement — become the reference for every trade-off we make later.

text
 1PRINCIPLE 1: FAIL FAST, FAIL CHEAP
 2  The fastest and most frequently failing step should run first.
 3
 4  Order: build → test → lint → architecture → AI review → spec audit
 5
 6  Rationale:
 7  - Build fail: most frequent, fastest (30 seconds)
 8  - Test fail: frequent, 2-5 minutes
 9  - AI review: rarely blocks, 2 minutes (but relatively expensive)
10  - Spec audit: rarely runs, 1 minute
11
12  Don't: run spec audit before tests — pointless if tests fail
13
14PRINCIPLE 2: PARALLEL WHERE POSSIBLE
15  Steps that aren't dependent on each other should run in parallel.
16
17  Parallel group A (blocking):
18    go build, go test, golangci-lint, architecture-check
19
20  Parallel group B (informational, non-blocking at first):
21    AI review, PR description generation
22
23  Total wall time: max(group A) + security-scan
24  Not: the sum of all steps
25
26PRINCIPLE 3: GRADUATED ENFORCEMENT
27  Not every failure should block merge from day one.
28
29  Level 1 (blocking from day one):
30    Build fail, test fail, critical security vulnerability
31
32  Level 2 (blocking after 2 sprints):
33    AI review CRITICAL issues, architecture violations
34
35  Level 3 (informational always):
36    AI review SUGGESTION, PR description quality

If you had to pick the one principle people violate most, it’s principle 1: never burn AI tokens reviewing code whose tests haven’t even passed.


02.2 Layer Architecture: 4 Layers, 1 Pipeline

Our pipeline is built in layers, from the cheapest and most deterministic to the most expensive and semantic. The layer diagram below shows all four layers along with the time and blocking status of each.

text
 1┌─────────────────────────────────────────────────────────────┐
 2│  LAYER 1: SYNTACTIC (Deterministic, Zero AI Cost)           │
 3│  go build | go test -race | go vet | golangci-lint          │
 4│  Time: 2-5 minutes | Blocking: YES from day 1               │
 5├─────────────────────────────────────────────────────────────┤
 6│  LAYER 2: SEMANTIC-STATIC (Custom Script, Zero AI Cost)     │
 7│  Architecture boundary | Import graph | Naming convention   │
 8│  Time: 30 seconds | Blocking: YES (sprint 2+)               │
 9├─────────────────────────────────────────────────────────────┤
10│  LAYER 3: SEMANTIC-AI (LLM-Powered, Low Cost)               │
11│  Copilot PR review | Claude error pattern check             │
12│  Time: 2 minutes (parallel) | Blocking: CRITICAL only       │
13├─────────────────────────────────────────────────────────────┤
14│  LAYER 4: SPEC COMPLIANCE (Spec Kit, Optional)              │
15│  specify audit | AC coverage | Contract validation          │
16│  Time: 1 minute | Blocking: if score < 80                   │
17└─────────────────────────────────────────────────────────────┘

Notice that only the top two layers are free of AI cost — this is why the majority of the validation load is deliberately placed in Layer 1 and 2, and AI only handles what static analysis can’t catch.


02.3 Full Pipeline Diagram

After understanding the layers conceptually, let’s see how everything connects in a single execution flow. The complete diagram below shows the trigger strategy, layer order, and the blocking points along with their parallel gate.

flowchart TD
    Push[Git Push / PR Open] --> Trigger

    subgraph Trigger["Trigger Strategy"]
        direction LR
        T1[push to main/develop] -->|full pipeline| Pipeline
        T2[PR opened/updated] -->|full pipeline + AI review| Pipeline
        T3[push to feature branch] -->|Layer 1 only| L1
    end

    subgraph L1["Layer 1: Syntactic"]
        direction LR
        Build[go build ./...] --> Test[go test -race -count=1 ./...]
        Test --> Vet[go vet ./...]
        Vet --> Lint[golangci-lint]
        Lint --> SecScan[gosec + gitleaks]
    end

    subgraph L2["Layer 2: Semantic-Static"]
        Arch[architecture-check.go]
        DepCheck[dependency guard]
    end

    subgraph L3["Layer 3: Semantic-AI (parallel)"]
        direction LR
        CopilotReview[Copilot PR Review]
        PRDesc[PR Description Generator]
        FailureAnalysis[Test Failure Analysis]
    end

    subgraph L4["Layer 4: Spec Compliance"]
        SpecAudit[specify audit]
    end

    Pipeline --> L1
    L1 -->|PASS| L2
    L1 -->|FAIL| BlockL1[❌ Block: Fix Build/Test/Lint]
    L2 -->|PASS| ParallelGate
    L2 -->|FAIL| BlockL2[⚠️ Block: Fix Architecture]

    ParallelGate --> L3
    ParallelGate --> L4

    L3 -->|CRITICAL| BlockL3[⚠️ Block: Fix Critical AI Issues]
    L3 -->|OK/SUGGESTION| PassL3[✅ AI Review Passed]

    L4 -->|score < 80| BlockL4[⚠️ Block: Spec Non-Compliant]
    L4 -->|score >= 80| PassL4[✅ Spec Compliant]

    PassL3 --> Merge
    PassL4 --> Merge
    BlockL3 -.->|after fix| Merge
    BlockL4 -.->|after fix| Merge
    Merge[✅ Ready to Merge]

    style L3 fill:#e8f4ff,stroke:#4a90d9
    style L4 fill:#e8f4ff,stroke:#4a90d9
    style CopilotReview fill:#dceeff,stroke:#4a90d9
    style PRDesc fill:#dceeff,stroke:#4a90d9
    style SpecAudit fill:#dceeff,stroke:#4a90d9

Diagram: Full AI-augmented pipeline — blue nodes are AI-powered steps

The most important point from this diagram: L3 and L4 branch from the same point (ParallelGate) after L1+L2 pass, so both run at the same time and save wall time.


02.4 GitHub Actions Job Structure

The diagram above needs to be translated into actual jobs in GitHub Actions. The workflow below is the full structure of all four layers, from the trigger to the spec-audit job — notice how needs orders the sequence and if limits when a job runs.

yaml
  1# .github/workflows/ci.yml — Full Structure
  2
  3name: CI — AI-Assisted Quality Pipeline
  4on:
  5  push:
  6    branches: [main, develop]
  7  pull_request:
  8    branches: [main, develop]
  9    types: [opened, synchronize, ready_for_review]
 10
 11# One env block for all jobs
 12env:
 13  GO_VERSION: '1.22'
 14
 15jobs:
 16  # ============================================================
 17  # LAYER 1: SYNTACTIC — Always runs, blocks everything
 18  # ============================================================
 19
 20  syntactic:
 21    name: "L1: Build, Test & Lint"
 22    runs-on: ubuntu-latest
 23    outputs:
 24      status: ${{ job.status }}
 25    steps:
 26      - uses: actions/checkout@v4
 27      - uses: actions/setup-go@v5
 28        with: { go-version: '${{ env.GO_VERSION }}', cache: true }
 29      - name: Build
 30        run: go build ./...
 31      - name: Test
 32        run: go test -race -count=1 -timeout=5m ./...
 33      - name: Coverage gate
 34        run: |
 35          go test -coverprofile=cov.out ./...
 36          COVERAGE=$(go tool cover -func=cov.out | grep total | awk '{print $3}' | tr -d '%')
 37          echo "Coverage: ${COVERAGE}%"
 38          awk "BEGIN {exit ($COVERAGE >= 70) ? 0 : 1}" || \
 39            { echo "❌ Coverage below 70%"; exit 1; }
 40      - name: Vet
 41        run: go vet ./...
 42      - name: Lint
 43        uses: golangci/golangci-lint-action@v6
 44      - name: Security scan
 45        run: |
 46          go install github.com/securego/gosec/v2/cmd/gosec@latest
 47          gosec -severity medium -no-fail ./...
 48
 49  # ============================================================
 50  # LAYER 2: SEMANTIC-STATIC — Runs after L1, blocks merge
 51  # ============================================================
 52
 53  architecture:
 54    name: "L2: Architecture Boundary Check"
 55    runs-on: ubuntu-latest
 56    needs: syntactic
 57    steps:
 58      - uses: actions/checkout@v4
 59      - uses: actions/setup-go@v5
 60        with: { go-version: '${{ env.GO_VERSION }}' }
 61      - name: Check layer boundaries
 62        run: go run ./scripts/check-architecture.go ./...
 63      - name: Check dependency guard
 64        run: |
 65          # Ensure no cross-service internal imports
 66          go run ./scripts/check-monorepo-boundaries.go ./...
 67
 68  # ============================================================
 69  # LAYER 3: SEMANTIC-AI — Parallel with L4, PR only
 70  # ============================================================
 71
 72  ai-review:
 73    name: "L3: AI Semantic Review"
 74    runs-on: ubuntu-latest
 75    needs: syntactic          # only runs after L1 passes
 76    if: |
 77      github.event_name == 'pull_request' &&
 78      !github.event.pull_request.draft
 79    permissions:
 80      pull-requests: write
 81      contents: read
 82    steps:
 83      - uses: actions/checkout@v4
 84        with: { fetch-depth: 0 }
 85      - name: Copilot PR Review
 86        uses: github/copilot-for-pull-requests@v1
 87        with:
 88          github-token: ${{ secrets.GITHUB_TOKEN }}
 89          review-instructions: |
 90            Review Go code per CLAUDE.md rules.
 91            CRITICAL (block): error not wrapped, float64 for money,
 92            architecture violations, _ ignoring errors.
 93            End with REVIEW_SCORE: X/100
 94
 95  pr-enrichment:
 96    name: "L3: PR Description & Metadata"
 97    runs-on: ubuntu-latest
 98    needs: syntactic
 99    if: |
100      github.event_name == 'pull_request' &&
101      github.event.action == 'opened'
102    permissions:
103      pull-requests: write
104      contents: read
105    env:
106      ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
107    steps:
108      - uses: actions/checkout@v4
109        with: { fetch-depth: 0 }
110      - name: Generate PR description
111        uses: actions/github-script@v7
112        with:
113          script: |
114            // Auto-generate PR description (details in Article 10)
115            console.log('PR description generation — see Article 10');
116
117  # ============================================================
118  # LAYER 4: SPEC COMPLIANCE — Parallel with L3, PR only
119  # ============================================================
120
121  spec-audit:
122    name: "L4: Spec Compliance Audit"
123    runs-on: ubuntu-latest
124    needs: syntactic
125    if: |
126      github.event_name == 'pull_request' &&
127      !github.event.pull_request.draft
128    steps:
129      - uses: actions/checkout@v4
130      - uses: actions/setup-node@v4
131        with: { node-version: '20' }
132      - name: Install specify CLI
133        run: npm install -g @santekno/spec-kit
134      - name: Run spec audit
135        run: specify audit --fail-on-score-below 80

The key to reading this workflow: ai-review and spec-audit both needs: syntactic (not needs: architecture), so both run in parallel after Layer 1 passes — exactly like the parallel gate in the earlier diagram.


02.5 Trigger Strategy: When Each Thing Runs

Not every pipeline needs to run on every event; the wrong trigger produces a slow and expensive pipeline. The breakdown below maps each event to which layer runs and its estimated time.

text
 1Not every pipeline needs to run on every event.
 2The wrong trigger = a slow and expensive pipeline.
 3
 4EVENT: push to feature branch
 5  Purpose: fast feedback for the developer currently coding
 6  Run: Layer 1 only (build + test + lint)
 7  Don't run: AI review (no PR), spec audit
 8  Time: 3-5 minutes
 9
10EVENT: PR opened or synchronize (new commit to PR)
11  Purpose: quality gate before code review
12  Run: Layer 1 + 2 + 3 + 4
13  Time: 7 minutes (L1+L2 sequential, L3+L4 parallel)
14
15EVENT: push to main or develop
16  Purpose: verify post-merge health
17  Run: Layer 1 + 2 (L3 and L4 not relevant — PR already reviewed)
18  Time: 5 minutes
19
20EVENT: PR marked ready_for_review (from draft)
21  Purpose: run the full pipeline for a PR that just became ready
22  Run: Layer 1 + 2 + 3 + 4 (full)
23  Time: 7 minutes
24
25EVENT: schedule (nightly)
26  Purpose: full health check + dependency update check
27  Run: Layer 1 + security deep scan
28  Time: 10 minutes (comprehensive scan)

The principle to take away: a push event to a feature branch only needs Layer 1 for fast feedback, whereas the full pipeline with AI only needs to run when there’s a PR that’s actually going to be reviewed.


02.6 Cost Architecture: Estimated Cost per Event

Before enabling the pipeline, it helps to calculate its cost so there are no surprises in the monthly bill. The breakdown below sums the cost per layer up to a monthly estimate for an 8-developer team.

text
 1Cost per pipeline run:
 2
 3Layer 1 (no AI cost):
 4  GitHub Actions: ~$0.002 (ubuntu runner, 5 minutes)
 5  Total L1: $0.002
 6
 7Layer 2 (no AI cost):
 8  GitHub Actions: ~$0.001 (30 seconds)
 9  Total L2: $0.001
10
11Layer 3 (AI cost):
12  Copilot PR review: already included in subscription
13  Claude PR description: ~500 tokens input, ~200 output
14    Cost: (500 × $0.80 + 200 × $4.00) / 1,000,000 = $0.0012
15  GitHub Actions: ~$0.001
16  Total L3: ~$0.002
17
18Layer 4 (no AI cost):
19  specify CLI: free
20  GitHub Actions: ~$0.001
21  Total L4: $0.001
22
23TOTAL per full pipeline run: ~$0.006
24
25Monthly estimate (24 PRs/week × 4 weeks = 96 PRs/month):
26  96 × $0.006 = $0.58/month
27  Plus GitHub Actions compute: ~$2/month
28
29  TOTAL: ~$2.60/month for an 8-developer team
30  (excluding the Copilot subscription you already have)

That ~$2.60/month figure is practically negligible compared to the engineer time saved — cost is not the limiting factor in the decision to adopt this pipeline.


02.7 Repository Structure for the Pipeline

The pipeline needs the support of a tidy repository structure so every script and config is easy to find. The directory structure below shows where the workflows, scripts, and config files are placed.

text
 1santekno-shop/
 2├── .github/
 3│   ├── workflows/
 4│   │   ├── ci.yml              ← main pipeline (this article)
 5│   │   ├── release.yml         ← release notes generator (article 11)
 6│   │   └── nightly.yml         ← scheduled security scan
 7│   ├── copilot-instructions.md ← AI review context
 8│   └── pull_request_template.md
 9├── scripts/
10│   ├── check-architecture.go   ← from Topic 3
11│   ├── check-monorepo-boundaries.go
12│   └── review-bot/
13│       └── main.go             ← custom AI review (article 5)
14├── CLAUDE.md                   ← universal context
15├── .golangci.yml               ← lint configuration
16├── .gitleaks.toml              ← secret scan rules
17└── go.work                     ← workspace (if monorepo)

Notice that all the supporting files — CLAUDE.md, lint config, architecture script — live inside the same repo, so the pipeline and its rules are versioned together with the code.


02.8 A Comprehensive .golangci.yml

Layer 1 is only as strong as its linter configuration. The .golangci.yml configuration below is the one we use in Santekno Shop — enabling linters for correctness, idiom, performance, security, and custom import rules.

yaml
 1# .golangci.yml — configuration for Santekno Shop
 2
 3run:
 4  timeout: 5m
 5  go: '1.22'
 6  tests: true
 7
 8linters:
 9  enable:
10    # Core correctness
11    - errcheck          # check unhandled errors
12    - govet             # suspicious constructs
13    - staticcheck       # comprehensive static analysis
14    - unused            # unused code
15
16    # Go idioms
17    - gofmt             # formatting
18    - goimports         # import ordering
19    - misspell          # typos
20
21    # Performance
22    - prealloc          # slice preallocation
23    - bodyclose         # HTTP response body close check
24
25    # Security
26    - gosec             # security issues
27    - exhaustive        # switch exhaustiveness
28
29    # Custom rules
30    - depguard          # forbidden import paths
31    - gomodguard        # module usage rules
32
33linters-settings:
34  depguard:
35    rules:
36      main:
37        deny:
38          # Ensure no cross-service internal imports
39          - pkg: "github.com/santekno/santekno-shop/services/*/internal"
40            desc: "Cross-service internal imports forbidden. Use Kafka events."
41          # Ensure no deprecated mock library
42          - pkg: "github.com/golang/mock"
43            desc: "Use go.uber.org/mock instead"
44
45  errcheck:
46    check-type-assertions: true
47    check-blank: true
48
49  govet:
50    enable-all: true
51
52issues:
53  exclude-rules:
54    # Test files may ignore errors
55    - path: _test\.go
56      linters: [errcheck]
57    # Generated files are excluded
58    - path: ".*\\.pb\\.go"
59      linters: [all]
60    - path: ".*mock.*\\.go"
61      linters: [all]
62
63  max-issues-per-linter: 50
64  max-same-issues: 3

The depguard section is the most valuable part here: it enforces the architecture boundary (the ban on cross-service internal imports) deterministically without a single cent of AI cost.


02.9 Secrets Management in CI

A pipeline that calls external APIs needs secrets managed securely. The block below summarizes which secrets are needed, how to set them via the GitHub CLI, and the best practices for managing them.

bash
 1# GitHub Secrets that need to be set up:
 2
 3# Required for AI review via the Claude API:
 4ANTHROPIC_API_KEY      # from console.anthropic.com
 5
 6# Required for secret scanning:
 7# (none — gitleaks and trufflehog don't need a secret)
 8
 9# Optional for extended features:
10SLACK_WEBHOOK          # for deployment notifications
11SONAR_TOKEN            # if using SonarQube
12
13# Setup via GitHub CLI:
14gh secret set ANTHROPIC_API_KEY --body "sk-ant-xxx"
15gh secret set SLACK_WEBHOOK --body "https://hooks.slack.com/xxx"
16
17# Verify:
18gh secret list
19
20# Security best practices for secrets in CI:
21# 1. Minimal permissions — API key only for what's needed
22# 2. Rotation schedule — rotate every 90 days
23# 3. Environment scoping — production secrets only in production env
24# 4. Audit logs — monitor usage in the Anthropic console
25
26# Environment protection (for production secrets):
27# GitHub → Settings → Environments → production → Protection rules
28# Required reviewers: [tech lead]
29# Restrict to protected branches: main only

The bottom line: only ANTHROPIC_API_KEY is truly required; secret scanning needs no credentials at all — and environment protection is an extra layer to separate production secrets from other branches.


02.10 Caching Strategy for Speed

Most of the pipeline time is spent downloading dependencies and rebuilding — caching cuts both. The configuration below shows four cache layers and the estimated time each one saves.

yaml
 1# Caches that are significant for a Go pipeline:
 2
 3# 1. Go module cache (most impactful, 30-60 seconds saved)
 4- uses: actions/setup-go@v5
 5  with:
 6    go-version: '1.22'
 7    cache: true          # this alone already caches go modules
 8
 9# 2. golangci-lint cache (10-20 seconds saved)
10- uses: golangci/golangci-lint-action@v6
11  with:
12    # Cache is already built into this action
13
14# 3. Build cache for custom scripts
15- uses: actions/cache@v4
16  with:
17    path: |
18      ~/.cache/go-build
19      ~/go/pkg/mod
20    key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
21    restore-keys: |
22      ${{ runner.os }}-go-
23
24# 4. Node modules for the specify CLI
25- uses: actions/cache@v4
26  with:
27    path: ~/.npm
28    key: ${{ runner.os }}-npm-specify-${{ hashFiles('package-lock.json') }}
29
30# Expected cache hit improvement:
31# Cold run (no cache): 8-12 minutes
32# Warm run (cache hit): 3-5 minutes
33# Savings: ~60% pipeline time

With proper caching, a warm run drops from 8-12 minutes to 3-5 minutes — a ~60% savings that’s immediately felt by developers every time they push.


02.11 Notification and Feedback Loop

Excessive notifications actually make the team ignore alerts. The notification pattern below is deliberately sparing — only notifying when truly necessary, with an example Slack workflow for a failure on the main branch.

yaml
 1# Useful notifications (not overwhelming):
 2
 3# Recommended pattern:
 4# - SUCCESS: no notification needed (don't distract the developer)
 5# - FAILURE on main: notify Slack channel #engineering-alerts
 6# - FAILURE on PR: GitHub comment (auto from Actions)
 7# - WEEKLY summary: digest in Slack #engineering
 8
 9# Workflow for Slack notification when main fails:
10- name: Notify on main branch failure
11  if: failure() && github.ref == 'refs/heads/main'
12  uses: 8398a7/action-slack@v3
13  with:
14    status: failure
15    fields: repo,message,commit,author,workflow
16    text: ':red_circle: Main branch CI failed! Check immediately.'
17  env:
18    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
19
20# GitHub comment for PR failure (built-in from Actions):
21# When a job fails, GitHub automatically shows it in the PR checks tab
22# The developer sees what failed right away without needing Slack

The principle is simple: the fewer notifications, the higher the attention per notification — don’t train the team to ignore alerts by flooding them with success messages.


02.12 Measuring Pipeline Health

The pipeline needs to be monitored just like a production service. The set of gh and jq commands below computes three pipeline health metrics directly from the GitHub Actions API.

bash
 1# Dashboard metrics to track:
 2
 3# 1. Pipeline success rate (target: > 95%)
 4# Via GitHub Actions API:
 5gh run list --workflow=ci.yml --json conclusion \
 6  | jq '[.[] | select(.conclusion != null)] | {
 7      total: length,
 8      success: [.[] | select(.conclusion == "success")] | length
 9    } | .success / .total * 100 | floor'
10
11# 2. Average pipeline duration (target: < 10 minutes)
12gh run list --workflow=ci.yml --json createdAt,updatedAt \
13  | jq '[.[] | ((.updatedAt | fromdateiso8601) - (.createdAt | fromdateiso8601))] | add / length / 60'
14
15# 3. Most common failure reason
16gh run list --workflow=ci.yml --status failure --json jobs \
17  | jq '[.[].jobs[] | select(.conclusion == "failure") | .name] | group_by(.) | map({job: .[0], count: length}) | sort_by(-.count) | .[0:5]'
18
19# Store metrics weekly:
20# Spreadsheet or Grafana dashboard
21# Review in the sprint retrospective

The “most common failure reason” metric is the most actionable: if one job dominates the failures, that’s where your tuning effort should be focused.


02.13 Blue/Green Pipeline Rollout

Replacing an active pipeline risks disrupting development if done all at once. The three-phase rollout strategy below introduces the new pipeline gradually — from shadow mode to full enforcement.

text
 1How to roll out a new pipeline without disrupting development:
 2
 3PHASE 1 (Week 1): Shadow mode
 4  The new pipeline runs in parallel with the old pipeline
 5  Its results are only logged, not blocking merge
 6  Purpose: validate the false positive rate
 7
 8PHASE 2 (Week 2-3): Warning mode
 9  The new pipeline replaces the old pipeline
10  Failures become GitHub comments (not blocking)
11  Developers can see them but aren't blocked
12  Purpose: developers get familiar with the output format
13
14PHASE 3 (Week 4+): Enforcement mode
15  CRITICAL issues block merge
16  SUGGESTION stays informational
17  Monitor: is anyone complaining about false positives?
18
19How to implement shadow mode:
20  continue-on-error: true  # on all AI-related steps
21
22  # After the shadow period:
23  # Remove continue-on-error from CRITICAL checks

The technical key to this rollout is continue-on-error: true: it’s this flag that lets the new pipeline “peek” without blocking anyone during the shadow and warning phases.


02.14 Pipeline as Code: Versioning and Review

The pipeline YAML is code, and it must be treated like code. The five practices below enforce that discipline — from review via PR to a rollback plan with git tags.

text
 1The pipeline YAML is code — it needs to be treated like code:
 2
 31. PR for pipeline changes
 4   Changes to .github/workflows/*.yml must go via PR
 5   Not directly pushed to main
 6
 72. Review by a senior developer
 8   The pipeline affects all developers — it needs careful review
 9
103. Testing in a branch before merge
11   Feature branch: make the pipeline change
12   Test: push a few commits, verify pipeline behavior
13   Merge: after confirmed working
14
154. CHANGELOG for the pipeline
16   Keep a changelog at .github/PIPELINE_CHANGELOG.md
17   Document every significant change
18
195. Rollback plan
20   Tag the pipeline state before a major change:
21   git tag pipeline-v1.0 (before AI review was added)
22   git tag pipeline-v2.0 (after AI review)
23
24   Rollback: git checkout pipeline-v1.0 -- .github/workflows/

Because pipeline changes affect the whole team, points 2 and 3 aren’t formalities: one wrong YAML can block everyone, so review and testing in a branch first is a must.


02.15 Pipeline Security Architecture

A pipeline that has access to secrets and write permissions is a new attack surface. The guidance below enforces least-privilege for permissions, secrets, third-party actions, and the runner.

text
 1Security considerations for a secure pipeline:
 2
 3PERMISSIONS (principle of least privilege):
 4  Default for all jobs:
 5    permissions:
 6      contents: read  # read-only by default
 7
 8  Override per-job only for those that need it:
 9    ai-review job:
10      permissions:
11        pull-requests: write  # to post comments
12        contents: read
13
14    pr-enrichment job:
15      permissions:
16        pull-requests: write
17        contents: read
18
19SECRETS:
20  - Don't expose secrets in logs
21  - Use ${{ secrets.XXX }}, not a hardcoded environment variable
22  - secrets.GITHUB_TOKEN: auto-generated, minimal scope
23
24THIRD-PARTY ACTIONS:
25  - Pin to a commit hash, not a floating tag
26  - Verified creator actions are safer
27
28  BAD:  uses: some-action@main
29  OK:   uses: some-action@v1
30  BEST: uses: some-action@abc123def456  # pinned commit
31
32RUNNER:
33  - ubuntu-latest for non-sensitive
34  - Self-hosted runner for sensitive secrets
35  - Isolated environment via container for high-security

One practice that’s often overlooked but crucial: pin third-party actions to a commit hash, not a tag — because a tag can be changed by its owner and become a supply chain attack vector.


02.16 Implementation Skeleton: Start Here

All the theory above will be executed starting from one minimal starter file. The template below is the starting point for ci.yml — only Layer 1 is active, with Layers 2-4 prepared as comments to be filled in in the following articles.

yaml
 1# STARTER TEMPLATE: .github/workflows/ci.yml
 2# Copy this, customize, and extend it in the following articles
 3
 4name: CI — Santekno Shop
 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  # Layer 1: Core Go checks
17  go-quality:
18    name: Go Quality Gates
19    runs-on: ubuntu-latest
20    steps:
21      - uses: actions/checkout@v4
22      - uses: actions/setup-go@v5
23        with: { go-version: '${{ env.GO_VERSION }}', cache: true }
24      - run: go build ./...
25      - run: go test -race -count=1 ./...
26      - run: go vet ./...
27      - uses: golangci/golangci-lint-action@v6
28
29  # Layer 2: Architecture (we add this in the next article)
30  # architecture-check: ...
31
32  # Layer 3: AI Review (article 03 onward)
33  # ai-review: ...
34
35  # Layer 4: Spec Audit (article 04)
36  # spec-audit: ...

Start from this skeleton: commit a working Layer 1 first, then uncomment Layers 2, 3, and 4 one by one in the following articles — exactly in the spirit of the “start with a skeleton, iterate” principle.


To put this pipeline in context, it helps to compare it with the standard pipelines in the Go community. The comparison below shows where the Santekno pipeline stands relative to the standard and advanced pipelines.

text
 1Comparison with standard pipelines in the Go community:
 2
 3STANDARD Go pipeline (most teams):
 4  ✅ build, test, vet, lint
 5  ❌ architecture enforcement
 6  ❌ AI review
 7  ❌ spec compliance
 8  ❌ PR description generation
 9
10ADVANCED Go pipeline (10% of teams):
11  ✅ everything above
12  ✅ security scanning (gosec, snyk)
13  ✅ coverage gate
14  ❌ AI review
15  ❌ spec compliance
16
17SANTEKNO AI-AUGMENTED (what we're building):
18  ✅ everything above
19  ✅ architecture enforcement (custom script)
20  ✅ AI semantic review (Copilot + Claude)
21  ✅ spec compliance (Spec Kit)
22  ✅ PR description generation
23  ✅ cost monitoring
24  ✅ graduated enforcement
25
26Competitive position:
27  Most Go teams in Indonesia: Standard pipeline
28  Our target: AI-Augmented, not yet widely adopted
29
30  Building this now = a 12-18 month head start
31  before this becomes the industry "standard"

The conclusion is clear: by building an AI-augmented pipeline now, your team gains a 12-18 month head start before this approach becomes the industry standard.


02.18 Implementation Timeline for Parts 1-4

This blueprint will be executed gradually throughout Topic 4. The roadmap below maps out what’s done in each article, from today’s skeleton to LLMOps in Part 4.

text
 1Implementation roadmap throughout Topic 4:
 2
 3NOW (Article 2):
 4  ✅ Architecture blueprint (this article)
 5  ✅ Skeleton workflow YAML
 6  ✅ Directory structure
 7
 8ARTICLE 3 (next):
 9  Claude Code setup in GitHub Actions
10  Environment, auth, CLAUDE.md access
11  Basic Claude API call from CI
12
13ARTICLE 4:
14  Spec validation integration
15  specify audit in the pipeline
16
17ARTICLES 5-9 (Part 2):
18  Quality gates: AI review, security, coverage, architecture
19
20ARTICLES 10-14 (Part 3):
21  Automation: PR description, changelog, migration check
22
23ARTICLES 15-20 (Part 4):
24  LLMOps: prompt versioning, cost monitoring, incident playbook

This roadmap also serves as an expectation contract: this article completes the architecture foundation, and the next article goes straight into setting up Claude in CI.


02.19 Architecture Tips & Gotchas

Before wrapping up, there are a few practical tips and traps worth noting when designing a pipeline architecture. The list below summarizes the three tips and three gotchas that most often determine whether an implementation succeeds.

💡 Tip 1: Start with a skeleton, iterate. Don’t build the full pipeline all at once. Start with a working Layer 1, commit to main, then add Layers 2, 3, 4 gradually.

💡 Tip 2: Test the pipeline in a feature branch. Before merging a pipeline change to main, test it in a feature branch with a few dummy commits to verify behavior.

💡 Tip 3: Comment your YAML. Pipeline YAML with no comments is hard to maintain 6 months later. Comment every job with its purpose and conditions.

⚠️ Gotcha 1: The wrong needs. If L3 doesn’t have needs: syntactic, it will run at the same time as L1 — and may fail because L1 isn’t done yet. Always define the dependency with needs.

⚠️ Gotcha 2: An if condition that’s too complex. If one job has 5+ conditions, split it into multiple jobs or use reusable workflows.

⚠️ Gotcha 3: No timeout. Jobs without a timeout can run forever if there’s an infinite loop or a stuck process. Always add timeout-minutes: 10.

If you can only remember one, remember Gotcha 1: the wrong needs is the most common cause of a pipeline that “sometimes passes, sometimes fails” for no clear reason.


02.20 Summary

The architecture blueprint we designed in this article is the foundation for all the implementation in Topic 4. Key decisions:

4 clear layers: Syntactic → Semantic-Static → Semantic-AI → Spec Compliance. Each layer has a different purpose and cost.

Parallel where possible: L3 and L4 run in parallel after L1+L2 finish. Total wall time ≤ 8 minutes.

Graduated enforcement: Warning first, block later. A pipeline that’s too strict from the start will be bypassed.

Predictable cost: ~$3/month for an 8-developer team with 24 PRs/week.

In the next article, we start implementation with the most critical step: setting up Claude Code in GitHub Actions.

Related Articles

💬 Comments