How I automated MY development in recent months
Edoardo Spadoni — Nethesis
my.nethesis.it — centralized management with Logto IdP and RBAC
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
- Push to main
- GitHub Actions → multi-platform build
- Automatic deploy on Render
- PostgreSQL shared across all components
- Redis for cache (backend) and queues (collect)
- 🔀 350+ commits
- 📄 7,586 lines OpenAPI
- 🧩 25 handler files
- 🗄 14 SQL migrations
- 📝 724 lines CLAUDE.md
- ⚡ 6 custom skills
- 👥 4 RBAC roles
- 🔧 5 components
Every time I need to add an endpoint or fix a bug…
- Read the bug report
- Search for the involved code (
grep -reverywhere) - Understand RBAC logic (who sees what?)
- Write the fix
- Test with 4 different tokens (Owner, Dist, Res, Cust)
gofmt+golangci-lint+go test- Update
openapi.yaml - Commit + PR with description
migration.sql+rollback.sql- Update
schema.sql - Go struct in
models/ - SQL query in
entities/ - Handler in
methods/ - Route in
main.go+ RBAC middleware - Update
openapi.yaml(7,586 lines…) - Test with 4 roles + lint + fmt
- Commit + PR
⏱️ Bug fix: ~30-45 min · New API: ~1-2 hours · Repeated x 25 endpoints
A terminal assistant that knows your project
$ npm install -g @anthropic-ai/claude-code
$ cd my/backend
$ claude
╭──────────────────────────────────────────────╮
│ ✻ Welcome to Claude Code! │
│ │
│ /help for available commands │
│ cwd: ~/my/backend │
╰──────────────────────────────────────────────╯
>📖 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
724 lines of rules — Claude reads them every session
// 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
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
The AI respects your project's rules only if you write them
Without CLAUDE.md → generic code. With CLAUDE.md → your code.
Claude explores the codebase, proposes a plan, waits for your OK
- 📁 Multi-file modifications
- 🏗 Architectural choices
- ✨ Non-trivial features
- ❓ Ambiguous requirements
- 🔍 Explores with Glob, Grep, Read
- 🧠 Analyzes existing patterns
- 📋 Proposes detailed plan
- ⏸️ Awaits approval
- ✍️ Only then: writes code
$ 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]Ask in plain English, Claude searches, correlates, and responds with file:line
$ 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_idBefore: grep -r "orgRole" . | head -50 → wall of text, you correlate manually
After: one question in plain language → structured response with context
🧠 Opus 4 (orchestrator)
↓
├─ ⚙️ backend-dev (Sonnet) — API, SQL, Go
├─ 🎨 frontend-dev (Sonnet) — Vue, TypeScript
├─ 🔒 security-eng (Haiku) — Vuln, OWASP
└─ 🔬 code-reviewer (Haiku) — Quality, rules
- 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
- 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
Each agent is a ~/.claude/agents/<name>.md file with role, tools, and instructions
---
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📋 Frontmatter — name, description, tools, model (sonnet/haiku/opus), color
🧠 Instructions — role, checklist, phased workflow. Each agent has its own specialized context and operates autonomously
Slash commands custom in .claude/skills/*/SKILL.md
- /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
> /pre-commit
Detecting components... Modified: backend/methods/alerts.go
backend → ✅ gofmt ✅ lint ✅ test (37 passed) ✅ openapi
— All checks passed.Each skill is a .claude/skills/<name>/SKILL.md file with frontmatter + instructions
---
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🔬 /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
- 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
- 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)
Claude sees the Figma design and generates Vue components — MCP = Model Context Protocol
$ claude mcp add --transport http figma https://mcp.figma.com/mcp
# → Browser opens for OAuth with Figma — done.$ 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<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>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 ☕
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
$ 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 autonomously...
Found endpoint, handler, SQL query
Understood the problem, proposed the plan
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] ✅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✅ gofmt
✅ golangci-lint
✅ go test (37 passed)
✅ redocly lint openapi.yaml
All checks passed.
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
- 🔍 Search for code:
grep -r, read 5-10 files - 🧠 Understand involved RBAC logic
- ✍️ Write the fix, test manually
- 🧪 4 curl with 4 different tokens
- 🔧 fmt + lint + test + openapi
- 📝 Commit message manually
⏱️ ~30-45 minutes
- 💬 Paste Andrea's message
- 🔍 Claude finds the code by itself
- 📋 Proposes the plan → I approve
- ✍️ Applies the fix
- ✅ /pre-commit automatic
- 📝 Commit message from diff
⏱️ ~2-3 minutes
🎯 Doesn't replace the developer — removes the boring part and lets you decide the strategy
# 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- Create a CLAUDE.md — even 20 lines are enough
- Explore — "how does X work in the project?"
- A simple fix — paste a bug report
- Add skills — for tasks you repeat
- 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.
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
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..."
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
Copied the Mattermost thread into Claude, in the NethLink directory
- 🔍 Analyzes the code — finds the connectivity check
- 📋 Proposes the plan — I approve → fix
- 🔀 Branch + commit + push — all automatic
- 📝 Issue + PR via gh — with description and links
Issue — NethServer/dev#7849
- Describes the problem, impact, root cause
- 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
One-time per project Write a good CLAUDE.md
Saved per task From ~1 hour to ~5 minutes
Break-even From the 3rd task you're in profit
2 hours today, hundreds saved tomorrow
from zero to hero
Edoardo Spadoni — Nethesis
Questions?
- MY Project: github.com/nethserver/my
- Claude Code: docs.anthropic.com
- Figma MCP: mcp.figma.com
Edoardo Spadoni — Nethesis