Skip to content

Instantly share code, notes, and snippets.

@yoseforb
Created February 11, 2026 22:39
Show Gist options
  • Select an option

  • Save yoseforb/8b1ea937cb756f0aec1b99e2f302b59d to your computer and use it in GitHub Desktop.

Select an option

Save yoseforb/8b1ea937cb756f0aec1b99e2f302b59d to your computer and use it in GitHub Desktop.

Development Workflow with Claude Code

Overview

This project uses Claude Code as an AI-powered pair programmer. The human developer directs strategy, approves all artifacts, and maintains quality control. Claude Code executes implementation following strict TDD discipline and mandatory quality gates.

The developer never surrenders decision-making authority. Every interface, every test, every implementation, and every commit requires explicit developer approval before the workflow advances. Claude Code proposes; the developer disposes.

Workflow Phases

Research --> ADRs --> Planning --> Implementation (TDD per task) --> Completion

Each phase produces artifacts that are committed to git. Each phase requires developer approval before moving to the next. There is no shortcutting -- skipping research leads to bad decisions, skipping planning leads to disorganized implementation, and skipping TDD leads to fragile code.


Phase 1: Research

Claude Code (documentation-specialist agent) researches libraries, patterns, and techniques relevant to the upcoming work. Research documents are created in ai-docs/research/ and committed to git like code.

Process:

  1. Developer identifies a topic that needs research (e.g., "we need image processing in Go").
  2. Claude Code researches the topic using web search, library documentation, and API references.
  3. A research document is created in ai-docs/research/ with findings, comparisons, trade-offs, and recommendations.
  4. Developer reviews the research document.
  5. Committed with docs(research): ... prefix.

What research documents contain:

  • Problem statement and constraints
  • Library/technique comparisons with pros and cons
  • API surface analysis and code examples
  • Performance characteristics and benchmarks
  • Integration considerations specific to this project
  • Recommendation with justification

Examples from this project's git history:

Document Purpose
go-image-libraries.md Compared stdlib, imaging, bimg, vipsgen for image processing
go-face-plate-detection-libraries.md Evaluated ML inference options for privacy detection
vipsgen-api-reference.md Detailed API reference for chosen image library
onnxruntime-go-api-reference.md API reference for ML inference runtime
scrfd-model-output-format.md SCRFD face detection model output tensor format
privacy-blur-implementations.md Blur techniques for privacy region redaction
nms-implementation.md Non-maximum suppression algorithm research
scaling-strategy.md Horizontal and vertical scaling approaches

Phase 2: Architecture Decision Records (ADRs)

Research produces understanding. ADRs crystallize that understanding into binding decisions. ADRs live in ai-docs/adr/ and serve a dual purpose: they document decisions for human developers AND they constrain Claude Code's behavior in future sessions.

Why ADRs are critical for AI-assisted development:

Claude Code has no memory between conversations. Without ADRs, every new session starts from zero -- Claude Code would re-evaluate decisions that were already made, potentially choosing different libraries, patterns, or approaches each time. ADRs solve this by being included in the project context (via CLAUDE.md references and the ai-docs/ directory). When Claude Code starts a new session, it reads the ADRs and follows those decisions instead of making its own.

This is how architectural decisions persist across conversations.

Process:

  1. Research reveals that a decision is needed (e.g., "which image library should we use?").
  2. Claude Code drafts an ADR documenting the decision, alternatives considered, and rationale.
  3. Developer reviews and approves the ADR.
  4. Committed with docs(adr): ... prefix.

ADR format:

  • Status: Accepted / Superseded / Deprecated
  • Context: The problem or decision point
  • Decision: What was decided
  • Consequences: Trade-offs and implications
  • Alternatives Considered: What was rejected and why

Examples from this project (13 ADRs):

ADR Decision
000 Go as implementation language
001 Pipes and Filters architecture pattern
002 Server-side image validation strategy
003 vipsgen (libvips) for image processing
004 ONNX format for ML models
005 onnxruntime_go for inference
006 SCRFD + YOLOv11 detection models
007 Four-stage pipeline architecture
008 Test-Driven Development
009 Classical/Detroit testing over Mockist/London
010 Dual-mode integration tests
011 Goa Design HTTP framework
012 Valkey over Redis (open-source, BSD-3-Clause)

Plus cross-project ADRs from follow-api (015, 016, 017, 022) that affect both services.


Phase 3: Planning

Before any implementation begins, a plan is created. Plans contain requirements, acceptance criteria, task breakdown, and dependencies. They live in the ai-docs/planning/ directory and follow a strict lifecycle.

Process:

  1. Developer identifies the next body of work.
  2. Claude Code creates a plan document in ai-docs/planning/backlog/.
  3. Developer reviews the plan: Are the tasks correct? Is the ordering right? Are acceptance criteria clear?
  4. When work begins, the plan moves to ai-docs/planning/active/.
  5. Committed with docs(planning): ... prefix.

Plan contents:

  • Goal and scope
  • Requirements and acceptance criteria
  • Task breakdown (ordered)
  • Dependencies between tasks
  • Integration considerations
  • Testing strategy

Plan lifecycle:

backlog/  -->  active/  -->  completed/  -->  archived/  -->  git rm
State Meaning
backlog/ Approved but not yet started
active/ Currently being implemented
completed/ All tasks done, developer confirmed
archived/ Retained temporarily for reference
git rm Permanently removed from the repository

Critical rule: old plans must be removed from git.

Completed and archived plans must eventually be git rm'd. This is not mere housekeeping -- it prevents a real and serious problem. Claude Code reads the entire ai-docs/ directory for context. If old plans remain, Claude Code may read them and follow outdated instructions, implement features that were descoped, or apply patterns that were superseded. Removing old plans keeps Claude Code focused exclusively on current, approved work.


Phase 4: Implementation (TDD per Task)

This is the core of the workflow. Each task from the active plan follows a strict TDD cycle with developer approval gates at every step. The cycle is: Interface, Stub, RED tests, GREEN implementation, Refactor.

Step 1: Create Interface

Claude Code (go-backend-engineer agent) creates the interface that defines the contract for the component being built.

  • The interface captures the behavioral contract without committing to implementation details.
  • Quality gates must pass with zero issues.
  • Developer reviews: Is this the right abstraction? Are the method signatures correct? Is the interface minimal (no speculative methods)?
  • Committed: feat(scope): add [InterfaceName] interface

Step 2: Create Stub Implementation

Claude Code creates a stub that satisfies the interface but does no real work.

  • Returns ErrNotImplemented or zero values.
  • Includes a compile-time interface check: var _ Interface = (*Impl)(nil)
  • Quality gates must pass with zero issues.
  • This may be part of the interface commit or a separate commit.

Step 3: Write RED Tests

Claude Code writes comprehensive tests that exercise the interface contract. These tests MUST genuinely fail -- this is the RED phase, and it is non-negotiable.

Test requirements:

  • Table-driven tests with t.Parallel().
  • Classical/Detroit style as mandated by ADR-009:
    • Hand-written fakes, NOT mock frameworks (testify/mock is only for external services like MinIO/Valkey).
    • Verify outcomes (state, return values), NOT interactions (method call counts, argument matchers).
    • Fakes implement the real interface with compile-time checks.
  • All quality gates must pass (formatting, linting, go mod tidy).

What is NOT acceptable in RED tests:

  • t.Skip() -- masks untested code as "passing"
  • Commented-out assertions -- tests that do not assert are not tests
  • // TODO placeholders for future assertions
  • Tests that pass against the stub -- if a test passes before implementation, it tests nothing

The RED phase proves that the tests are meaningful. A test that never fails provides no confidence when it passes.

Developer reviews:

  • Are these the right tests for this component?
  • Do they cover edge cases, error paths, and boundary conditions?
  • Are they Classical style (state verification)?
  • Do they genuinely fail against the stub?

Committed: test(scope): add [Component] TDD tests (RED phase)

Step 4: Implement GREEN

Claude Code implements the minimum functionality required to make all RED tests pass.

  • The goal is correctness, not elegance. Get the tests green first.
  • ALL tests in the entire project must pass: go test -race -cover ./...
  • Quality gates must pass with zero issues.
  • Developer reviews the implementation for correctness and approach.
  • Committed: feat(scope): implement [Component] (GREEN phase)

Step 5: Refactor

With passing tests as a safety net, Claude Code refactors the implementation for clarity, performance, or better structure.

  • All tests must continue to pass after refactoring.
  • Quality gates must pass with zero issues.
  • Developer reviews the refactored code.
  • Committed: refactor(scope): [description]

Repeat

Steps 1-5 repeat for each task in the active plan. The developer controls the pace and may reorder tasks, add tasks, or adjust scope based on what is learned during implementation.


Quality Gates

These gates are mandatory before the developer looks at any code. This is non-negotiable. Code that does not pass all gates is not ready for human review.

# 1. Format: gofumpt for strict Go formatting, golines for 80-char line limit
gofumpt -w . && golines -w --max-len=80 .

# 2. Lint: go vet + extensive custom linters (including nilaway for nil-safety)
go vet ./... && ./custom-gcl run -c .golangci-custom.yml ./... --fix

# 3. Test: race detector enabled, coverage reported
go test -race -cover ./...

# 4. Dependencies: ensure go.mod and go.sum are clean
go mod tidy

Why the custom linter configuration matters:

The file .golangci-custom.yml configures an extensive set of linters that go far beyond the Go defaults. This includes nilaway (which performs whole-program nil-safety analysis), along with rules for error handling, naming conventions, complexity limits, and more. These linters are a primary mechanism for producing high-quality code. They catch entire categories of bugs that tests alone would miss.

Skipping the linters -- or ignoring their output -- defeats the purpose of the entire workflow.

Rule: Zero tolerance. Every warning, every error must be resolved. There is no "we'll fix that later."


Commit Discipline

Every step in the workflow produces a commit. Commits are atomic, focused, and descriptive.

Format:

type(scope): description

Types:

Type When
feat New functionality (interfaces, implementations)
fix Bug fixes
refactor Code restructuring without behavior change
docs Documentation (research, ADRs, plans, architecture)
test Test additions or modifications
style Formatting, whitespace, import ordering
improve Enhancements to existing functionality

Documentation commit prefixes:

  • docs(research): ... -- research documents
  • docs(adr): ... -- architecture decision records
  • docs(planning): ... -- plans (creation, moves, completion, removal)
  • docs(architecture): ... -- architecture specifications

Rules:

  • One commit per logical change. Do not bundle unrelated changes.
  • The agent commits only after the developer has approved the artifact.
  • Plans and research are committed to git with the same discipline as code.

Plan Completion

When all tasks in an active plan are done:

  1. Developer confirms that all acceptance criteria are met.
  2. Plan file moves from ai-docs/planning/active/ to ai-docs/planning/completed/.
  3. Committed: docs(planning): mark [plan] as completed
  4. After a reasonable period, move from completed/ to archived/.
  5. Eventually, git rm the archived plan to remove it from the repository entirely.

The git rm step is not optional. See the explanation in Phase 3 about why old plans must be removed.


Why This Workflow Works

Developer stays in control. Every artifact -- interface, test, implementation, ADR, plan -- requires explicit approval. Claude Code proposes, the developer decides. There is no autonomous execution.

Quality is enforced mechanically. The custom linter suite and race-enabled tests catch issues before human review. The developer's time is spent on design decisions and architectural review, not catching nil dereferences or formatting inconsistencies.

TDD prevents regressions. The RED-GREEN-REFACTOR cycle guarantees that tests are meaningful (they failed before implementation) and that the code works (it passes after implementation). Refactoring is safe because the tests provide a safety net.

ADRs enforce decisions across sessions. Claude Code has no persistent memory. ADRs in ai-docs/adr/ are read at the start of every session, ensuring that past decisions are respected. Without ADRs, each session would risk re-litigating settled questions.

Plan hygiene prevents context pollution. Removing completed plans from git ensures that Claude Code only reads current, approved instructions. Old plans in the repository are a source of confusion and contradictory guidance.

Classical testing ensures maintainability. Fakes over mocks means tests verify what the code does, not how it does it. When implementation details change during refactoring, tests that verify state continue to pass. Tests that verify interactions break on every refactor, creating false failures and eroding confidence.

Everything is in git. Research, ADRs, plans, and code all live in the same repository with the same commit discipline. There is a complete, auditable history of every decision and every change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment