Skip to content

Instantly share code, notes, and snippets.

@stephdl
Created February 11, 2026 07:35
Show Gist options
  • Select an option

  • Save stephdl/c6cbf00888169c22bb78309573441edf to your computer and use it in GitHub Desktop.

Select an option

Save stephdl/c6cbf00888169c22bb78309573441edf to your computer and use it in GitHub Desktop.
Claude Code: from zero to hero

Claude Code: from zero to hero

How I automated MY development in recent months

Edoardo Spadoni — Nethesis


🏗 The MY Project

my.nethesis.it — centralized management with Logto IdP and RBAC

Architecture

my/
  backend/   Go REST API — Gin, JWT, Logto
  collect/   Go inventory — Workers, Redis
  sync/      Go CLI — Cobra, Logto RBAC sync
  frontend/  Vue 3 + TypeScript — Vite, Pinia
  proxy/     nginx reverse proxy

🚀 Deploy:

  • Push to main
  • GitHub Actions → multi-platform build
  • Automatic deploy on Render

🗄 Infrastructure:

  • PostgreSQL shared across all components
  • Redis for cache (backend) and queues (collect)

📊 The Numbers

  • 🔀 350+ commits
  • 📄 7,586 lines OpenAPI
  • 🧩 25 handler files
  • 🗄 14 SQL migrations
  • 📝 724 lines CLAUDE.md
  • ⚡ 6 custom skills
  • 👥 4 RBAC roles
  • 🔧 5 components

😩 The Workflow BEFORE Claude

Every time I need to add an endpoint or fix a bug…

🐛 Fixing a bug

  1. Read the bug report
  2. Search for the involved code (grep -r everywhere)
  3. Understand RBAC logic (who sees what?)
  4. Write the fix
  5. Test with 4 different tokens (Owner, Dist, Res, Cust)
  6. gofmt + golangci-lint + go test
  7. Update openapi.yaml
  8. Commit + PR with description

🆕 New API

  1. migration.sql + rollback.sql
  2. Update schema.sql
  3. Go struct in models/
  4. SQL query in entities/
  5. Handler in methods/
  6. Route in main.go + RBAC middleware
  7. Update openapi.yaml (7,586 lines…)
  8. Test with 4 roles + lint + fmt
  9. Commit + PR

⏱️ Bug fix: ~30-45 min · New API: ~1-2 hours · Repeated x 25 endpoints


🤖 Enter Claude Code

A terminal assistant that knows your project

Setup — one command

$ npm install -g @anthropic-ai/claude-code
$ cd my/backend
$ claude

╭──────────────────────────────────────────────╮
│ ✻ Welcome to Claude Code!                   │
│                                              │
│ /help for available commands                │
│ cwd: ~/my/backend                           │
╰──────────────────────────────────────────────╯
>

Capabilities

📖 Reads your code

  • Understands architecture, patterns, conventions

🔧 Modifies files

  • Writes code, creates migrations, updates docs

💻 Executes commands

  • go test, git commit, make, curl

🧠 Everything starts from one file: CLAUDE.md


📄 CLAUDE.md — the contract with AI

724 lines of rules — Claude reads them every session

❌ Without rules

// This was previously handled by the
// old auth system
func GetAlerts(c *gin.Context) {
    // Legacy compatibility wrapper
    query := "SELECT * FROM alerts WHERE "
        + "org=" + c.Param("org")
    err := fmt.Errorf("Failed to retrieve")
    c.JSON(500, gin.H{"error": err.Error()})
}

Issues: SQL injection · uppercase error · temporary comments · no pattern

✅ With CLAUDE.md

func GetAlerts(c *gin.Context) {
    claims := jwt.ExtractClaims(c)
    params := helpers.BuildPaginationParams(c)
    alerts, total, err := entities.GetAlerts(
        claims.OrgRole, claims.OrganizationID,
        params.Page, params.PageSize)
    if err != nil {
        response.Error(c, 500,
            "failed to retrieve alerts")
        return
    }
    response.Success(c, 200, gin.H{
        "alerts": alerts,
        "pagination":
            helpers.BuildPaginationInfoWithSorting(
                params, total)})
}

Benefits: Parameterized query · lowercase error · JWT claims · standard response


⚠️ Golden Rule

The AI respects your project's rules only if you write them

Without CLAUDE.md → generic code. With CLAUDE.md → your code.


🗺 Plan Mode — think first, then write

Claude explores the codebase, proposes a plan, waits for your OK

When it activates

  • 📁 Multi-file modifications
  • 🏗 Architectural choices
  • ✨ Non-trivial features
  • ❓ Ambiguous requirements

The workflow

  1. 🔍 Explores with Glob, Grep, Read
  2. 🧠 Analyzes existing patterns
  3. 📋 Proposes detailed plan
  4. ⏸️ Awaits approval
  5. ✍️ Only then: writes code

Terminal Example

$ claude
> Add endpoint for alerts

⟡ Entering plan mode...

📋 Plan:
1. Migration 014_alerts.sql + rollback
2. Model in models/alerts.go
3. Entity SQL in entities/alerts.go
4. Handler in methods/alerts.go
5. Route in main.go with RBAC
6. Update openapi.yaml
7. Run /pre-commit

Approve? [Y/n]

🔍 Explore — intelligent grep

Ask in plain English, Claude searches, correlates, and responds with file:line

Terminal Example

$ claude
> How does RBAC filtering work on organization endpoints?

⟡ Agent Explore — searching...
  Glob: backend/middleware/*.go
  Grep: "orgRole" in backend/entities/
  Read: backend/middleware/rbac.go
  Read: backend/entities/distributors.go

📍 Result:
middleware/rbac.go:28 → RequirePermission() checks user_permissions from JWT
middleware/rbac.go:45 → RequireOrgRole() filters by org_role (owner/distributor/...)
entities/distributors.go → switch claims.OrgRole to filter SQL queries
entities/resellers.go → same pattern: owner sees all, others filtered by org_id

Before vs After

Before: grep -r "orgRole" . | head -50 → wall of text, you correlate manually

After: one question in plain language → structured response with context


🤝 Agents — the virtual team

🧠 Opus 4 (orchestrator)
  ↓
  ├─ ⚙️ backend-dev (Sonnet) — API, SQL, Go
  ├─ 🎨 frontend-dev (Sonnet) — Vue, TypeScript
  ├─ 🔒 security-eng (Haiku) — Vuln, OWASP
  └─ 🔬 code-reviewer (Haiku) — Quality, rules

How it works

  • Opus decides strategy and coordinates work
  • Launches specialized agents in parallel
  • Each agent has dedicated tools (Read, Write, Bash, DB…)
  • Results are aggregated by Opus

💰 Why different models?

  • Opus (expensive) → only strategic decisions
  • Sonnet (medium) → complex tasks: writing code, APIs
  • Haiku (cheap) → simple tasks: review, scan

💡 Flat subscription with fixed token budget — lightweight models = more requests per day


🔧 Agents — the definition file

Each agent is a ~/.claude/agents/<name>.md file with role, tools, and instructions

~/.claude/agents/backend-developer.md

---
name: backend-developer
description: Senior backend engineer for scalable API development
tools: Read, Write, MultiEdit, Bash, Docker, database, redis, postgresql
model: sonnet
color: cyan
---

You are a senior backend developer with deep expertise in Go, SQL, APIs.

When invoked:
1. Query context for existing API architecture and DB schemas
2. Review current backend patterns and service dependencies
3. Analyze performance and security constraints
4. Begin implementation following established standards

Key Points

📋 Frontmattername, description, tools, model (sonnet/haiku/opus), color

🧠 Instructions — role, checklist, phased workflow. Each agent has its own specialized context and operates autonomously


⚡ Skills — custom automation

Slash commands custom in .claude/skills/*/SKILL.md

Available Skills

  • /review — 🔬 Code review vs CLAUDE.md rules — auto-invoked
  • /pre-commit — ✅ Detect component → fmt + lint + test + docs
  • /db-migration — 🗄 SQL + rollback + schema + Go model
  • /api-endpoint — 🔧 Route + handler + entity + OpenAPI
  • /test-api — 🧪 Test with 4 RBAC tokens automatically
  • /dev-setup — 🚀 Complete dev environment setup from scratch

Example: /pre-commit

> /pre-commit

Detecting components... Modified: backend/methods/alerts.go

backend → ✅ gofmt ✅ lint ✅ test (37 passed) ✅ openapi

— All checks passed.

📄 Skills — anatomy of a file

Each skill is a .claude/skills/<name>/SKILL.md file with frontmatter + instructions

.claude/skills/pre-commit/SKILL.md

---
name: pre-commit
description: Detect modified components and run pre-commit checks
user-invocable: true              # activatable with /pre-commit
disable-model-invocation: true    # manual only, not auto
---

# Pre-Commit

## Instructions

1. Detect modified components: git diff --name-only HEAD
2. Classify: backend/ → Go, frontend/ → Vue, collect/ → Go...
3. Run pre-commit per component (sequential, stop on fail)
4. Report results: pass/fail per component

Special Skill: /review

🔬 /review — the only one with disable-model-invocation: false — Claude calls it automatically after every significant modification

📂 6 skills in .claude/skills/ — 1 auto-invocable, 5 manual (/command) — all with step-by-step instructions


🤔 Agents vs Skills — what's the difference?

🤝 Agent

  • Separate process — forked by Claude
  • Has its own context, tools, model
  • Operates in parallel with other agents
  • Communicates results to Opus which coordinates
  • Ideal for complex and autonomous tasks

Location: ~/.claude/agents/backend-developer.md

⚡ Skill

  • Same process — executed by Claude directly
  • Shares context and current conversation
  • Sequential execution, step-by-step
  • Instructions injected into active prompt
  • Ideal for repeatable workflows

Location: .claude/skills/pre-commit/SKILL.md

🧠 Agent = delegation (works independently) · ⚡ Skill = recipe (Claude follows instructions in active context)


🎨 Figma MCP — design to code

Claude sees the Figma design and generates Vue components — MCP = Model Context Protocol

Setup — one command

$ claude mcp add --transport http figma https://mcp.figma.com/mcp
# → Browser opens for OAuth with Figma — done.

Input → Output Example

$ claude
> Implement this component:
  https://figma.com/design/abc123/MY?node-id=142-8891

🖼 get_screenshot → "sees" the design
📐 get_design_context → layout, styles
✍️ Generates Vue 3 component

Output — AlertCard.vue

<template>
  <div class="alert-card" :class="severityClass">
    <component :is="severityIcon" />
    <div class="alert-content">
      <p>{{ alert.message }}</p>
      <span>{{ formatTime(alert.created_at) }}</span>
    </div>
    <button v-if="!alert.acknowledged"
            @click="$emit('acknowledge')">
      Confirm
    </button>
  </div>
</template>

🎯 Ok, but in practice?

CLAUDE.md, Plan Mode, Explore, Agents, Skills, Figma MCP…

All this serves to automate daily work

Let's see a real example: a request on Mattermost ☕


💬 Real Example — Andrea's message

Andrea Leardini on Mattermost:

"The GET /filters/applications/systems returns all systems instead of only those associated with some application. Should we align with the other APIs?"

⬇️ I copy the message, paste it into Claude

Terminal

$ claude
> The GET /filters/applications/systems returns... Should we align with the other APIs?

🎬 And now Claude does everything by itself. Let's see what happens...


🔍 Claude in action — Step 1: Explore + Plan

Claude autonomously...

🔍 3 seconds

Found endpoint, handler, SQL query

🧠 5 seconds

Understood the problem, proposed the plan

⏸️ Awaits

My OK before touching code

⟡ Searching for the endpoint...
  Grep: "filters/applications/systems" in backend/
  Read: backend/main.go:147 → route definition
  Read: backend/methods/filters.go:89 → handler GetFilterApplicationsSystems
  Read: backend/entities/filters.go:156 → SQL query (returns ALL systems!)

⟡ Entering plan mode...

📋 Plan:
1. Modify entities/filters.go → add JOIN with applications
   to filter only systems that have at least one associated application
2. Update the comment in the SQL query
3. Run /pre-commit to verify
4. Commit with reference to the fix

Approve? [Y/n] ✅

✍️ Claude in action — Step 2: Fix + Commit

git diff — The applied fix

SELECT DISTINCT s.id, s.name
FROM systems s
+JOIN application_systems AS apps
+  ON apps.system_id = s.id
WHERE s.organization_id = $1

/pre-commit

✅ gofmt
✅ golangci-lint
✅ go test (37 passed)
✅ redocly lint openapi.yaml

All checks passed.

Automatic commit

git commit -m "$(cat <<'EOF'
fix: filter systems endpoint to return
only application-associated systems

The GET /filters/applications/systems
was returning all systems instead of
only those linked to an application.

Add JOIN with application_systems table
to filter correctly.
EOF
)"

✅ Fix + test + lint + commit in ~2 minutes


⏱️ Before vs After

😩 Without Claude

  1. 🔍 Search for code: grep -r, read 5-10 files
  2. 🧠 Understand involved RBAC logic
  3. ✍️ Write the fix, test manually
  4. 🧪 4 curl with 4 different tokens
  5. 🔧 fmt + lint + test + openapi
  6. 📝 Commit message manually

⏱️ ~30-45 minutes

🤖 With Claude

  1. 💬 Paste Andrea's message
  2. 🔍 Claude finds the code by itself
  3. 📋 Proposes the plan → I approve
  4. ✍️ Applies the fix
  5. ✅ /pre-commit automatic
  6. 📝 Commit message from diff

⏱️ ~2-3 minutes

🎯 Doesn't replace the developer — removes the boring part and lets you decide the strategy


🚀 Try it

Setup in 2 minutes

# 1. Install
npm install -g @anthropic-ai/claude-code

# 2. Enter the project
cd your-project && claude

# 3. (optional) Figma
claude mcp add --transport http \
  figma https://mcp.figma.com/mcp

📝 First steps

  1. Create a CLAUDE.md — even 20 lines are enough
  2. Explore — "how does X work in the project?"
  3. A simple fix — paste a bug report
  4. Add skills — for tasks you repeat
  5. Iterate the CLAUDE.md — wrong output? Add a rule

💡 The secret: CLAUDE.md evolves with the project. The more rules you write, the better the output becomes. It's an investment that pays off with every session.


🎁 Bonus 1 — how I made these slides

The prompt I gave Claude to generate these slides 👇

$ claude
> Create a Slidev presentation for the development team.
  Goal: explain how I used Claude Code to automate
  MY project development in recent months.
  
  Show: project architecture, pain points before Claude,
  the claude command, CLAUDE.md with before/after comparison,
  Plan Mode, Explore, dedicated Agents with economical models,
  custom Skills, Figma MCP.
  
  Close with a real example: Andrea Leardini's message
  on Mattermost about a bug in GET /filters/applications/systems,
  copied and pasted into Claude that finds, plans, fixes and commits.
  
  Use emojis, v-clicks, make it fun. Max 15 minutes.

📊 Result: 25 slides, generated and refined in ~30 minutes of conversation with Claude


🎁 Bonus 2 — the NethLink case

Mattermost thread:

"NethLink 1.4.1 doesn't start in the office. Log: ERR_BAD_REQUEST 429 on google.com. Works at home. WebRTC works. Only NethLink is broken..."

🔍 The problem

NethLink verifies connection with a GET to google.com — downloads the entire page. Everyone in the office → same IP → Google responds 429 Too Many Requests

What I did

Copied the Mattermost thread into Claude, in the NethLink directory

  1. 🔍 Analyzes the code — finds the connectivity check
  2. 📋 Proposes the plan — I approve → fix
  3. 🔀 Branch + commit + push — all automatic
  4. 📝 Issue + PR via gh — with description and links

Results

IssueNethServer/dev#7849

  • Describes the problem, impact, root cause

PRNethServer/nethlink#87

  • Complete fix, linked to issue

💬 Mattermost thread → 🤖 Claude → 🔀 Branch + Fix + Issue + PR — all from a copy-paste

⏱️ Manual: read thread, understand bug, search code, fix, branch, commit, push, issue, PR → ~1 hour · With Claude: ~5 minutes


📈 The investment that pays off

2 hours

One-time per project Write a good CLAUDE.md

55 min

Saved per task From ~1 hour to ~5 minutes

3rd task

Break-even From the 3rd task you're in profit

2 hours today, hundreds saved tomorrow


Thank you! 🎉

from zero to hero

Edoardo Spadoni — Nethesis


Q&A ❓

Questions?

  • MY Project: github.com/nethserver/my
  • Claude Code: docs.anthropic.com
  • Figma MCP: mcp.figma.com

Edoardo Spadoni — Nethesis

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