Development Guide
Table of Contents
- Development Environment Setup
- Project Structure
- Development Workflow
- Coding Standards
- Testing Guidelines
- Debugging
- Contributing
- Common Tasks
Development Environment Setup
Prerequisites
Required Software
# Go 1.24+
wget https://go.dev/dl/go1.24.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.24.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
# Docker & Docker Compose
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
sudo usermod -aG docker $USER
# PostgreSQL Client
sudo apt-get install postgresql-client
# Make
sudo apt-get install build-essential
# Git
sudo apt-get install gitDevelopment Tools
# Air for hot reload
go install github.com/air-verse/air@latest
# Migrate for database migrations
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
# Swagger for API docs
go install github.com/swaggo/swag/cmd/swag@latest
# GolangCI-Lint for linting
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2
# Mockery for test mocks
go install github.com/vektra/mockery/v2@latestLocal Setup
1. Clone Repository
git clone https://github.com/your-org/ms-project.git
cd ms-project2. Install Dependencies
# Go dependencies
go mod download
go mod tidy
# Verify installation
go mod verify3. Environment Configuration
# Copy environment template
cp .env.example .env
# Edit with your local settings
vim .env
# Required variables for local development
DATABASE_URL=postgresql://postgres:password@localhost:5432/ms_project_dev?sslmode=disable
REDIS_HOST=localhost
REDIS_PORT=6379
JWT_SECRET=local-development-secret-min-32-characters
NODE_ENV=development
PORT=80804. Database Setup
# Start PostgreSQL (using Docker)
docker run -d \
--name ms-project-postgres \
-e POSTGRES_DB=ms_project_dev \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=password \
-p 5432:5432 \
postgres:15-alpine
# Run migrations
make migrate-up
# Seed development data (optional)
make seed-dev5. Start Redis
docker run -d \
--name ms-project-redis \
-p 6379:6379 \
redis:7-alpine6. Run Application
# Using Air for hot reload
air
# Or using go run
go run cmd/server/main.go
# Or using make
make run-dev7. Verify Setup
# Health check
curl http://localhost:8080/health
# API status
curl http://localhost:8080/api/v1/statusProject Structure
ms-project/
├── cmd/ # Application entrypoints
│ ├── server/ # Main API server
│ │ └── main.go
│ └── migration/ # Migration tool
│ └── main.go
├── internal/ # Private application code
│ ├── config/ # Configuration management
│ │ └── config.go
│ ├── database/ # Database connection
│ │ └── connection.go
│ ├── dto/ # Data Transfer Objects
│ │ ├── project.go
│ │ └── task.go
│ ├── enums/ # Enumerations
│ │ └── status.go
│ ├── errors/ # Custom error types
│ │ └── errors.go
│ ├── events/ # Event system
│ │ └── emitter.go
│ ├── handlers/ # HTTP handlers
│ │ ├── project_handler.go
│ │ └── task_handler.go
│ ├── middleware/ # HTTP middleware
│ │ ├── auth.go
│ │ └── logger.go
│ ├── models/ # Domain models
│ │ ├── base.go
│ │ ├── project.go
│ │ └── task.go
│ ├── routes/ # Route definitions
│ │ └── routes.go
│ ├── services/ # Business logic
│ │ ├── project_service.go
│ │ └── task_service.go
│ └── utils/ # Utility functions
│ └── ulid.go
├── migrations/ # Database migrations
│ ├── 001_initial.up.sql
│ └── 001_initial.down.sql
├── tests/ # Test files
│ ├── integration/ # Integration tests
│ ├── unit/ # Unit tests
│ └── testutil/ # Test utilities
├── docs/ # Documentation
├── scripts/ # Utility scripts
├── .env.example # Environment template
├── .gitignore
├── Dockerfile
├── Makefile
├── go.mod
├── go.sum
└── README.mdDevelopment Workflow
Git Workflow
Branch Strategy
main # Production-ready code
├── develop # Integration branch
├── feature/[name] # New features
├── bugfix/[name] # Bug fixes
├── hotfix/[name] # Emergency fixes
└── release/[version] # Release preparationCreating a Feature
# 1. Create feature branch
git checkout develop
git pull origin develop
git checkout -b feature/add-notifications
# 2. Make changes
# ... code changes ...
# 3. Commit with conventional commits
git add .
git commit -m "feat: add notification system for task updates"
# 4. Push and create PR
git push origin feature/add-notificationsCommit Message Format
<type>(<scope>): <subject>
<body>
<footer>Types:
feat: New featurefix: Bug fixdocs: Documentationstyle: Code stylerefactor: Code refactoringtest: Testingchore: Maintenance
Examples:
feat(tasks): add priority filtering
fix(projects): resolve date validation issue
docs(api): update endpoint documentation
refactor(services): extract common validation logicDevelopment Commands
# Common make commands
make help # Show available commands
make run # Run application
make test # Run all tests
make test-unit # Run unit tests only
make test-integration # Run integration tests
make lint # Run linter
make fmt # Format code
make build # Build binary
make docker-build # Build Docker image
make migrate-up # Run migrations
make migrate-down # Rollback migrations
make swagger # Generate Swagger docs
make coverage # Generate test coverage
make clean # Clean build artifactsCoding Standards
Go Code Style
File Structure
package services
import (
// Standard library imports
"context"
"fmt"
"time"
// External imports
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
// Internal imports
"ms-project/internal/models"
"ms-project/internal/dto"
)Naming Conventions
// Constants - PascalCase
const MaxRetryAttempts = 3
// Variables - camelCase
var defaultTimeout = 30 * time.Second
// Functions/Methods - PascalCase for exported, camelCase for private
func CreateProject(dto CreateProjectDTO) (*Project, error) {}
func validateInput(input string) error {}
// Interfaces - PascalCase with "er" suffix
type ProjectCreator interface {
Create(project *Project) error
}
// Structs - PascalCase
type ProjectService struct {
db *gorm.DB
cache CacheService
}
// Struct fields - PascalCase for exported, camelCase for private
type Project struct {
ID string // Exported
Name string // Exported
isValidated bool // Private
}Error Handling
// Always check errors immediately
result, err := someFunction()
if err != nil {
return nil, fmt.Errorf("failed to execute someFunction: %w", err)
}
// Custom errors
type ValidationError struct {
Field string
Message string
}
func (e ValidationError) Error() string {
return fmt.Sprintf("validation error on field %s: %s", e.Field, e.Message)
}
// Error wrapping
if err := db.Create(&project).Error; err != nil {
return fmt.Errorf("failed to create project: %w", err)
}Context Usage
// Always accept context as first parameter
func (s *ProjectService) GetProject(ctx context.Context, id string) (*Project, error) {
// Use context for timeouts
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
// Pass context to database operations
var project Project
err := s.db.WithContext(ctx).First(&project, id).Error
return &project, err
}Defer Usage
func processFile(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close() // Always defer cleanup
// Process file...
return nil
}Documentation
Package Documentation
// Package services provides business logic implementations
// for the MS-Project application. It contains service layers
// that coordinate between handlers and repositories.
package servicesFunction Documentation
// CreateProject creates a new project with the given details.
// It validates the input, generates a unique code, and persists
// the project to the database.
//
// Parameters:
// - ctx: Context for request cancellation
// - dto: Data transfer object containing project details
//
// Returns:
// - *Project: The created project
// - error: Error if creation fails
func CreateProject(ctx context.Context, dto CreateProjectDTO) (*Project, error) {
// Implementation
}Struct Documentation
// ProjectService handles business logic for project operations.
// It coordinates between the HTTP handlers and the data layer,
// implementing validation, authorization, and business rules.
type ProjectService struct {
db *gorm.DB // Database connection
cache CacheService // Caching layer
eventEmitter EventEmitter // Event publishing
}Testing Guidelines
Unit Testing
Test File Structure
// project_service_test.go
package services_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func TestProjectService_CreateProject(t *testing.T) {
// Arrange
mockDB := new(MockDatabase)
service := NewProjectService(mockDB)
dto := CreateProjectDTO{
Name: "Test Project",
CustomerID: "customer123",
}
mockDB.On("Create", mock.Anything).Return(nil)
// Act
project, err := service.CreateProject(dto)
// Assert
assert.NoError(t, err)
assert.NotNil(t, project)
assert.Equal(t, "Test Project", project.Name)
mockDB.AssertExpectations(t)
}Table-Driven Tests
func TestValidateProjectName(t *testing.T) {
tests := []struct {
name string
input string
wantError bool
}{
{"valid name", "Project Alpha", false},
{"empty name", "", true},
{"too short", "ab", true},
{"too long", strings.Repeat("a", 256), true},
{"special chars", "Project @#$", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateProjectName(tt.input)
if tt.wantError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}Integration Testing
Database Testing
func TestProjectRepository_Integration(t *testing.T) {
// Skip if not integration test
if testing.Short() {
t.Skip("Skipping integration test")
}
// Setup test database
db := testutil.SetupTestDB(t)
defer testutil.TeardownTestDB(db)
repo := NewProjectRepository(db)
t.Run("Create and Retrieve", func(t *testing.T) {
// Create project
project := &Project{
Name: "Integration Test",
CustomerID: "customer123",
}
err := repo.Create(project)
assert.NoError(t, err)
assert.NotEmpty(t, project.ID)
// Retrieve project
retrieved, err := repo.FindByID(project.ID)
assert.NoError(t, err)
assert.Equal(t, project.Name, retrieved.Name)
})
}API Testing
func TestProjectAPI_CreateProject(t *testing.T) {
// Setup test server
app := setupTestApp()
// Prepare request
payload := map[string]interface{}{
"name": "API Test Project",
"customerId": "customer123",
}
body, _ := json.Marshal(payload)
req := httptest.NewRequest("POST", "/api/v1/projects", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer " + testToken)
// Execute request
resp, err := app.Test(req, -1)
// Assert
assert.NoError(t, err)
assert.Equal(t, 201, resp.StatusCode)
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
assert.NotNil(t, result["data"])
}Test Coverage
# Run tests with coverage
go test -coverprofile=coverage.out ./...
# View coverage report
go tool cover -html=coverage.out
# Coverage requirements
# - Unit tests: > 80%
# - Integration tests: > 60%
# - Critical paths: > 90%Debugging
Local Debugging
Using Delve
# Install Delve
go install github.com/go-delve/delve/cmd/dlv@latest
# Debug application
dlv debug cmd/server/main.go
# Debug specific test
dlv test ./internal/services -- -test.run TestProjectService
# Common Delve commands
(dlv) break main.go:42 # Set breakpoint
(dlv) continue # Continue execution
(dlv) next # Next line
(dlv) step # Step into
(dlv) print variableName # Print variable
(dlv) locals # Show local variables
(dlv) stack # Show stack traceVS Code Configuration
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Server",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/server",
"env": {
"DATABASE_URL": "postgresql://postgres:password@localhost:5432/ms_project_dev"
},
"args": []
},
{
"name": "Debug Test",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${workspaceFolder}/tests/unit",
"args": ["-test.run", "TestProjectService"]
}
]
}Logging
Structured Logging
import "github.com/rs/zerolog/log"
// Configure logger
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
// Log with context
log.Info().
Str("projectId", project.ID).
Str("userId", userID).
Msg("Project created successfully")
// Log errors
log.Error().
Err(err).
Str("projectId", projectID).
Msg("Failed to update project")
// Debug logging
log.Debug().
Interface("dto", dto).
Msg("Processing request")Request Logging Middleware
func LoggerMiddleware() fiber.Handler {
return func(c *fiber.Ctx) error {
start := time.Now()
// Process request
err := c.Next()
// Log request
log.Info().
Str("method", c.Method()).
Str("path", c.Path()).
Int("status", c.Response().StatusCode()).
Dur("latency", time.Since(start)).
Str("ip", c.IP()).
Msg("Request processed")
return err
}
}Performance Profiling
CPU Profiling
import _ "net/http/pprof"
// Add pprof endpoints
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Profile CPU
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30Memory Profiling
# Capture heap profile
go tool pprof http://localhost:6060/debug/pprof/heap
# Analyze allocations
go tool pprof -alloc_space http://localhost:6060/debug/pprof/allocsContributing
Pull Request Process
1. Fork and Clone
# Fork on GitHub, then:
git clone https://github.com/your-username/ms-project.git
cd ms-project
git remote add upstream https://github.com/original-org/ms-project.git2. Create Branch
git checkout -b feature/your-feature3. Make Changes
- Write code following coding standards
- Add tests for new functionality
- Update documentation
4. Test
# Run tests
make test
# Run linter
make lint
# Check formatting
make fmt5. Commit
git add .
git commit -m "feat: add amazing feature"6. Push and PR
git push origin feature/your-feature
# Create PR on GitHubCode Review Checklist
- Code follows project style guide
- Tests are included and passing
- Documentation is updated
- No sensitive data in code
- Error handling is proper
- Performance impact considered
- Security implications reviewed
- Breaking changes documented
Release Process
1. Version Bump
# Update version in go.mod, configs, etc.
make version VERSION=v1.2.02. Generate Changelog
# Generate changelog from commits
make changelog3. Create Release Branch
git checkout -b release/v1.2.04. Testing
# Run full test suite
make test-all
# Run integration tests
make test-integration
# Performance tests
make test-performance5. Tag and Release
git tag v1.2.0
git push origin v1.2.0Common Tasks
Database Operations
# Create new migration
migrate create -ext sql -dir migrations -seq create_users_table
# Run migrations
make migrate-up
# Rollback last migration
make migrate-down
# Reset database
make db-reset
# Seed development data
make seed-devAPI Documentation
# Generate Swagger docs
swag init -g cmd/server/main.go
# Update after API changes
make swagger
# View docs
open http://localhost:8080/swagger/index.htmlDependency Management
# Add new dependency
go get github.com/some/package
# Update dependencies
go get -u ./...
# Clean unused dependencies
go mod tidy
# Vendor dependencies
go mod vendorDocker Operations
# Build image
docker build -t ms-project:dev .
# Run container
docker run -p 8080:8080 ms-project:dev
# Docker Compose
docker-compose up -d
docker-compose logs -f
docker-compose downBenchmarking
// Write benchmark
func BenchmarkCreateProject(b *testing.B) {
service := NewProjectService(db)
dto := CreateProjectDTO{Name: "Benchmark"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
service.CreateProject(dto)
}
}
// Run benchmarks
go test -bench=. -benchmem ./...Troubleshooting
Common Issues
Port Already in Use
# Find process using port
lsof -i :8080
# Kill process
kill -9 <PID>Database Connection Failed
# Check PostgreSQL status
pg_isready -h localhost -p 5432
# Check credentials
psql -h localhost -U postgres -d ms_project_devModule Issues
# Clear module cache
go clean -modcache
# Re-download dependencies
go mod downloadBuild Failures
# Clean build cache
go clean -cache
# Rebuild
go build -a ./...Resources
Documentation
Tools
Learning Resources
Conclusion
This development guide provides comprehensive information for:
- Setting up development environment
- Following coding standards
- Writing and running tests
- Debugging applications
- Contributing to the project
Follow these guidelines to maintain code quality and consistency across the project.