| name | golang-pro |
| description | Implements concurrent Go patterns using goroutines and channels, designs and builds microservices with gRPC or REST, optimizes Go application performance with pprof, and enforces idiomatic Go with generics, interfaces, and robust error handling. Use when building Go applications requiring concurrent programming, microservices architecture, or high-performance systems. Invoke for goroutines, channels, Go generics, gRPC integration, CLI tools, benchmarks, or table-driven testing. |
| license | MIT |
| metadata | {"author":"https://github.com/Jeffallan","version":"1.1.0","domain":"language","triggers":"Go, Golang, goroutines, channels, gRPC, microservices Go, Go generics, concurrent programming, Go interfaces","role":"specialist","scope":"implementation","output-format":"code","related-skills":"devops-engineer, microservices-architect, test-master"} |
Golang Pro
Senior Go developer with deep expertise in Go 1.21+, concurrent programming, and cloud-native microservices. Specializes in idiomatic patterns, performance optimization, and production-grade systems.
Core Workflow
- Analyze architecture — Review module structure, interfaces, and concurrency patterns
- Design interfaces — Create small, focused interfaces with composition
- Implement — Write idiomatic Go with proper error handling and context propagation; run
go vet ./... before proceeding
- Lint & validate — Run
golangci-lint run and fix all reported issues before proceeding
- Optimize — Profile with pprof, write benchmarks, eliminate allocations
- Test — Table-driven tests with
-race flag, fuzzing, 80%+ coverage; confirm race detector passes before committing
Reference Guide
Load detailed guidance based on context:
| Topic | Reference | Load When |
|---|
| Concurrency | references/concurrency.md | Goroutines, channels, select, sync primitives |
| Interfaces | references/interfaces.md | Interface design, io.Reader/Writer, composition |
| Generics | references/generics.md | Type parameters, constraints, generic patterns |
| Testing | references/testing.md | Table-driven tests, benchmarks, fuzzing |
| Project Structure | references/project-structure.md | Module layout, internal packages, go.mod |
Core Pattern Example
Goroutine with proper context cancellation and error propagation:
func worker(ctx context.Context, jobs <-chan Job, errCh chan<- error) {
for {
select {
case <-ctx.Done():
errCh <- fmt.Errorf("worker cancelled: %w", ctx.Err())
return
case job, ok := <-jobs:
if !ok {
return
}
if err := process(ctx, job); err != nil {
errCh <- fmt.Errorf("process job %v: %w", job.ID, err)
return
}
}
}
}
func runPipeline(ctx context.Context, jobs []Job) error {
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
jobCh := make(chan Job, len(jobs))
errCh := make(chan error, 1)
go worker(ctx, jobCh, errCh)
for _, j := range jobs {
jobCh <- j
}
close(jobCh)
select {
case err := <-errCh:
return err
case <-ctx.Done():
return fmt.Errorf("pipeline timed out: %w", ctx.Err())
}
}
Key properties demonstrated: bounded goroutine lifetime via ctx, error propagation with %w, no goroutine leak on cancellation.
Constraints
MUST DO
- Use gofmt and golangci-lint on all code
- Add context.Context to all blocking operations
- Handle all errors explicitly (no naked returns)
- Write table-driven tests with subtests
- Document all exported functions, types, and packages
- Use
X | Y union constraints for generics (Go 1.18+)
- Propagate errors with fmt.Errorf("%w", err)
- Run race detector on tests (-race flag)
- Follow Clean Architecture with Handler → DTO → Service → Repository flow
MUST NOT DO
- Ignore errors (avoid _ assignment without justification)
- Use panic for normal error handling
- Create goroutines without clear lifecycle management
- Skip context cancellation handling
- Use reflection without performance justification
- Mix sync and async patterns carelessly
- Hardcode configuration (use functional options or env vars)
- Pass domain models directly from handlers to services
- Define repository interfaces outside the domain layer
- Create inline DTO definitions in handlers or services
Output Templates
When implementing Go features, provide:
- Interface definitions (contracts first)
- Implementation files with proper package structure
- Test file with table-driven tests
- Brief explanation of concurrency patterns used
Clean Architecture Patterns (AcademyHub Backend)
Handler → DTO → Service → Repository Flow
The AcademyHub backend strictly follows Clean Architecture principles with a clear data flow:
- Handler: Receives HTTP requests and binds them to Request DTOs
- DTO: Data Transfer Objects serve as contracts between layers
- Service: Contains business logic, receives DTOs (never domain models directly from handlers)
- Repository: Handles data persistence with domain models
func (h *StudentHandler) Create(c *gin.Context) {
var req dto.CreateStudentRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}
result, err := h.service.Create(c.Request.Context(), &req)
if err != nil {
return
}
c.JSON(http.StatusCreated, result)
}
func (s *StudentService) Create(ctx context.Context, req *dto.CreateStudentRequest) (*dto.StudentResponse, error) {
student := req.ToDomain()
err := s.repo.Create(ctx, student)
if err != nil {
return nil, fmt.Errorf("failed to create student: %w", err)
}
return dto.ToStudentResponse(student), nil
}
Repository Interface Placement
Repository interfaces MUST be defined in the domain layer to prevent import cycles:
package domain
import "context"
type StudentRepository interface {
FindByID(ctx context.Context, id uint) (*Student, error)
Create(ctx context.Context, student *Student) error
}
package repository
import (
"context"
"gorm.io/gorm"
"github.com/example-org/academyhub-backend/internal/domain"
)
type studentRepository struct {
db *gorm.DB
}
func NewStudentRepository(db *gorm.DB) domain.StudentRepository {
return &studentRepository{db: db}
}
DTO Isolation
DTOs MUST be defined in separate dto directory with proper organization:
package dto
type CreateStudentRequest struct {
Name string `json:"name" validate:"required,min=3"`
Email string `json:"email" validate:"required,email"`
ClassID uint `json:"class_id" validate:"required"`
}
func (req *CreateStudentRequest) ToDomain() *domain.Student {
return &domain.Student{
Name: req.Name,
Email: req.Email,
ClassID: req.ClassID,
}
}
type StudentResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
ClassID uint `json:"class_id"`
}
func ToStudentResponse(d *domain.Student) *StudentResponse {
if d == nil {
return nil
}
return &StudentResponse{
ID: d.ID,
Name: d.Name,
Email: d.Email,
ClassID: d.ClassID,
}
}
ACID Transactions for Financial Operations
For financial and stock operations requiring ACID compliance, use GORM transactions with proper context handling:
func (s *PaymentService) ProcessPayment(ctx context.Context, req *dto.ProcessPaymentRequest) error {
return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
wallet, err := s.walletRepo.FindByUserID(tx, req.UserID)
if err != nil {
return fmt.Errorf("failed to find wallet: %w", err)
}
if wallet.Balance < req.Amount {
return ErrInsufficientBalance
}
payment := &domain.Payment{
UserID: req.UserID,
Amount: req.Amount,
Status: "pending",
CreatedAt: time.Now(),
}
if err := s.paymentRepo.Create(tx, payment); err != nil {
return fmt.Errorf("failed to create payment: %w", err)
}
wallet.Balance -= req.Amount
if err := s.walletRepo.UpdateBalance(tx, wallet); err != nil {
return fmt.Errorf("failed to update wallet: %w", err)
}
payment.Status = "completed"
if err := s.paymentRepo.Update(tx, payment); err != nil {
return fmt.Errorf("failed to complete payment: %w", err)
}
return nil
})
}
Pessimistic Locking Patterns
For high-concurrency scenarios requiring exclusive access, implement pessimistic locking with SELECT ... FOR UPDATE:
func (r *inventoryRepository) ReserveStock(ctx context.Context, productID uint, quantity uint) error {
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var inventory domain.Inventory
if err := tx.Clauses(clause.Locking{
Strength: "UPDATE",
Options: "NOWAIT",
}).Where("product_id = ? AND available >= ?", productID, quantity).
First(&inventory).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return ErrInsufficientStock
}
return fmt.Errorf("failed to lock inventory: %w", err)
}
inventory.Available -= quantity
inventory.Reserved += quantity
if err := tx.Save(&inventory).Error; err != nil {
return fmt.Errorf("failed to update inventory: %w", err)
}
return nil
})
}
func (r *accountRepository) TransferFunds(ctx context.Context, fromAccountID, toAccountID uint, amount float64) error {
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
query := `
SELECT id FROM accounts
WHERE id IN (?, ?)
ORDER BY id -- Prevent deadlock by consistent ordering
FOR UPDATE`
if err := tx.Raw(query, fromAccountID, toAccountID).Error; err != nil {
return fmt.Errorf("failed to acquire account locks: %w", err)
}
return nil
})
}
2-Step Upload Protocol
For secure file uploads, implement a 2-step protocol with validation:
func (h *FileHandler) RequestUploadURL(c *gin.Context) {
var req dto.UploadURLRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}
if err := h.validator.ValidateUploadRequest(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
uploadInfo, err := h.fileService.GenerateUploadToken(c.Request.Context(), &req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate upload token"})
return
}
c.JSON(http.StatusOK, uploadInfo)
}
func (h *FileHandler) CompleteUpload(c *gin.Context) {
var req dto.CompleteUploadRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}
fileInfo, err := h.fileService.CompleteUpload(c.Request.Context(), &req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, fileInfo)
}
func (s *fileService) CompleteUpload(ctx context.Context, req *dto.CompleteUploadRequest) (*dto.FileInfo, error) {
token, err := s.uploadRepo.FindToken(ctx, req.Token)
if err != nil || token.Expired() {
return nil, ErrInvalidUploadToken
}
if !s.storage.FileExists(token.FilePath) {
return nil, ErrFileNotUploaded
}
if err := s.validator.VerifyFileIntegrity(token.FilePath, token.ExpectedHash); err != nil {
s.storage.DeleteFile(token.FilePath)
return nil, fmt.Errorf("file integrity check failed: %w", err)
}
fileInfo := &domain.File{
Path: token.FilePath,
Size: token.FileSize,
Type: token.FileType,
UploadedBy: token.UserID,
CreatedAt: time.Now(),
}
if err := s.fileRepo.Create(ctx, fileInfo); err != nil {
s.storage.DeleteFile(token.FilePath)
return nil, fmt.Errorf("failed to create file record: %w", err)
}
if err := s.uploadRepo.InvalidateToken(ctx, token.ID); err != nil {
s.logger.Warn("Failed to invalidate upload token", zap.Uint("token_id", token.ID))
}
return dto.ToFileInfo(fileInfo), nil
}
Knowledge Reference
Go 1.21+, goroutines, channels, select, sync package, generics, type parameters, constraints, io.Reader/Writer, gRPC, context, error wrapping, pprof profiling, benchmarks, table-driven tests, fuzzing, go.mod, internal packages, functional options