12 KiB
12 KiB
Development Guide — go-first / URPI
This guide covers local development setup, workflows, and best practices.
🛠️ Prerequisites
Required Tools
| Tool | Version | Install |
|---|---|---|
| Go | 1.23+ | brew install go / apt install golang |
| Node.js | 20+ | nvm install 20 / fnm install 20 |
| Docker | 24+ | docker.com |
| Git | 2.40+ | brew install git / apt install git |
Recommended Tools
- IDE: VS Code with Go + TypeScript extensions
- API Client: HTTPie, Postman, or VS Code REST Client
- Database: TablePlus, DBeaver, or pgAdmin
- Terminal: Warp, Ghostty, or Kitty
🚀 Getting Started
1. Clone & Setup
git clone https://github.com/dnoulet/go-first.git
cd go-first
# Install all dependencies
make install
# This runs:
# - go mod download
# - cd web && npm ci
2. Start Development Databases
# Start PostgreSQL + Redis only
docker compose up -d postgres redis
# Verify
docker compose ps
docker compose exec postgres pg_isready -U postgres
docker compose exec redis redis-cli ping
3. Configure Local Environment
cp .env.local.example .env.local
# Edit if needed (defaults work for Docker databases)
vim .env.local
4. Run Development Servers
# Option A: Both together (recommended)
make dev
# Option B: Separate terminals
# Terminal 1 - Backend
make dev-backend
# Terminal 2 - Frontend
make dev-frontend
5. Verify
| Service | URL |
|---|---|
| Frontend | http://localhost:5173 |
| Backend API | http://localhost:8080/api/v1 |
| Health Check | http://localhost:8080/api/v1/health |
| PostgreSQL | localhost:5432 |
| Redis | localhost:6379 |
🔄 Development Workflow
Backend Development
# Hot reload with air (already configured in .air.toml)
make dev-backend
# Or manually:
go run ./cmd/server
Adding a New Endpoint
- Model — Add types in
internal/model/models.go - Service — Create service in
internal/service/ - Handler — Add handler in
internal/handler/handler.go - Routes — Register in
handler.RegisterRoutes() - Tests — Add tests in
*_test.gofiles
Frontend Development
# Hot module replacement (Vite)
make dev-frontend
# Or manually:
cd web && npm run dev
Adding a New Page/Section
- Types — Add to
web/src/types/index.ts - API — Add endpoint to
web/src/services/api.ts - Hook — Create custom hook in
web/src/hooks/ - Component — Create in
web/src/components/sections/ - Register — Import and add to
web/src/App.tsx
Code Generation
# Generate mocks for testing
make generate
# This runs: go generate ./...
# Uses: github.com/golang/mock/mockgen
🧪 Testing
Backend Tests
# Run all tests with coverage
make test-backend
# Run specific package
go test -v ./internal/service/...
# Run with race detector
go test -race ./...
# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
Frontend Tests
# Run tests
make test-frontend
# Or manually:
cd web && npm test
# With UI
cd web && npm test -- --ui
# Coverage
cd web && npm test -- --coverage
E2E Tests (Optional)
# Install Playwright
cd web && npx playwright install
# Run E2E tests
cd web && npm run test:e2e
🔍 Code Quality
Linting
# All linters
make lint
# Backend only
make lint-backend
# Runs: golangci-lint run ./...
# Frontend only
make lint-frontend
# Runs: eslint + prettier check
Formatting
# Format all code
make fmt
# Backend
go fmt ./...
goimports -w .
# Frontend
cd web && npm run format
Pre-commit Hooks
# Install pre-commit
pip install pre-commit
pre-commit install
# Run manually
pre-commit run --all-files
.pre-commit-config.yaml
repos:
- repo: https://github.com/golangci/golangci-lint
rev: v1.56.0
hooks:
- id: golangci-lint
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
hooks:
- id: prettier
types: [typescript, json, yaml, markdown]
- repo: local
hooks:
- id: go-fmt
name: go fmt
entry: go fmt ./...
language: system
types: [go]
🗃️ Database Migrations
Creating Migrations
# Create new migration
make db-migrate-create name=add_users_table
# This creates:
# migrations/000001_add_users_table.up.sql
# migrations/000001_add_users_table.down.sql
Running Migrations
# Apply all pending
make db-migrate-up
# Rollback last
make db-migrate-down
# Check status
migrate -path ./migrations -database "$DSN" version
Migration Best Practices
- Up migrations: Add columns, tables, indexes
- Down migrations: Reverse exactly (drop what you added)
- Never modify applied migrations
- Test both up and down locally
- Name clearly:
add_user_email_index, notfix_db
🐳 Docker Development
Build Locally
# Build production image
make docker-build
# Run production image locally
make docker-run
# Or with docker-compose (includes DB)
docker compose up -d --build
Debugging in Container
# Run with shell
docker run -it --rm -p 8080:8080 ghcr.io/dnoulet/go-first:latest sh
# Or attach to running
docker exec -it go-first-app sh
Development with Docker Compose Override
Create docker-compose.override.yml:
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/app
- /app/web/node_modules
command: air -c .air.toml
environment:
- SERVER_MODE=debug
🎨 Frontend Architecture
Component Structure
web/src/components/
├── ui/ # Reusable primitives (Button, Card, Input, Badge)
├── layout/ # Page structure (Header, Footer)
├── sections/ # Page sections (Hero, Services, Projects, etc.)
└── forms/ # Form-specific components
State Management
- Server state:
useApihook (React Query pattern) - Local state:
useState,useReducer - Forms: Controlled components with validation
- No global store (Redux/Zustand) — keep it simple
Styling
- Tailwind CSS utility-first
- Custom design system in
tailwind.config.js - CSS variables for theming (dark mode ready)
- Component variants via
clsx+tailwind-merge
Adding Icons
# Use lucide-react (already installed)
import { IconName } from 'lucide-react'
# Or add to public/ and use <img>
🔧 Backend Architecture
Clean Architecture Layers
cmd/server/ # Entry point, wiring
internal/
├── config/ # Configuration (YAML + env)
├── handler/ # HTTP layer (Gin)
├── middleware/ # Cross-cutting concerns
├── model/ # Domain models, DTOs
└── service/ # Business logic
Dependency Injection
Services are instantiated in main.go and passed to handlers:
skillService := service.NewSkillService()
h := handler.NewHandler(skillService, ...)
Adding Database Support
- Add DB config to
config.yaml - Create
internal/repository/package - Implement interfaces in
service/ - Use
database/sqlorsqlxorgorm - Add migrations
Middleware Chain
router.Use(
middleware.Recovery(), // Panic recovery
middleware.Logger(), // Request logging
middleware.SecurityHeaders(), // Security headers
middleware.CORS(...), // CORS
)
📝 Git Workflow
Branch Naming
| Type | Pattern | Example |
|---|---|---|
| Feature | feat/short-description |
feat/contact-form-validation |
| Fix | fix/short-description |
fix/cors-header-missing |
| Docs | docs/short-description |
docs/update-api-docs |
| Refactor | refactor/short-description |
refactor/service-layer |
| Chore | chore/short-description |
chore/update-dependencies |
Commit Messages
Follow Conventional Commits:
feat: add contact form validation
fix: resolve CORS issue on production
docs: update API documentation
refactor: extract service interfaces
chore: update Go dependencies
Pull Requests
- Create feature branch from
main - Make changes with tests
- Run
make lint && make test - Push and open PR
- Require: CI passing, 1 approval
- Squash merge to
main
🐛 Debugging
Backend Debugging
# Delve debugger
dlv debug ./cmd/server
# VS Code launch.json
{
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cmd/server",
"env": {
"SERVER_MODE": "debug"
}
}
Frontend Debugging
# React DevTools (browser extension)
# Redux DevTools (if using)
# VS Code debug config:
{
"type": "chrome",
"request": "launch",
"url": "http://localhost:5173",
"webRoot": "${workspaceFolder}/web/src"
}
Database Debugging
# Connect to PostgreSQL
docker compose exec postgres psql -U postgres -d go_first
# Common queries
\dt # List tables
\d users # Describe table
SELECT * FROM skills; # Query data
📦 Dependency Management
Go Dependencies
# Add dependency
go get github.com/new/package
# Update all
go get -u ./...
go mod tidy
# Check outdated
make check-deps
Frontend Dependencies
# Add dependency
cd web && npm install package-name
# Add dev dependency
cd web && npm install -D package-name
# Update
cd web && npm update
# Audit
cd web && npm audit
🚀 Performance Tips
Backend
- Use connection pooling (configured in config)
- Add Redis caching for expensive queries
- Enable gzip compression (NGINX)
- Use
pproffor profiling:import _ "net/http/pprof"
Frontend
- Code splitting (Vite does this automatically)
- Lazy load sections:
const Section = lazy(() => import('./Section')) - Optimize images (WebP, proper sizing)
- Use
React.memofor expensive components
🔐 Environment Variables
Required for Development
| Variable | Description | Default |
|---|---|---|
SERVER_MODE |
debug or release |
debug |
DATABASE_HOST |
PostgreSQL host | localhost |
DATABASE_PASSWORD |
PostgreSQL password | postgres |
JWT_SECRET |
JWT signing key | dev-secret |
CORS_ALLOWED_ORIGINS |
Comma-separated origins | http://localhost:3000,http://localhost:5173 |
Optional
| Variable | Description |
|---|---|
EMAIL_SMTP_* |
SMTP config for contact form emails |
LOGGING_LEVEL |
debug, info, warn, error |
REDIS_* |
Redis connection (for caching/sessions) |
📚 Resources
❓ FAQ
Q: How do I add a new API field? A: Update model → service → handler → frontend types → component
Q: Why is my frontend not updating? A: Check Vite HMR, clear browser cache, check console for errors
Q: How do I test email sending locally? A: Use mock email service (default) or configure Mailhog/SMTP
Q: Can I use a different database?
A: Yes, implement repository interface in internal/repository/
Q: How do I deploy a preview environment?
A: Use make deploy-staging or create PR preview with GitHub Actions