596 lines
No EOL
12 KiB
Markdown
596 lines
No EOL
12 KiB
Markdown
# 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](https://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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
cp .env.local.example .env.local
|
|
|
|
# Edit if needed (defaults work for Docker databases)
|
|
vim .env.local
|
|
```
|
|
|
|
### 4. Run Development Servers
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# Hot reload with air (already configured in .air.toml)
|
|
make dev-backend
|
|
|
|
# Or manually:
|
|
go run ./cmd/server
|
|
```
|
|
|
|
#### Adding a New Endpoint
|
|
|
|
1. **Model** — Add types in `internal/model/models.go`
|
|
2. **Service** — Create service in `internal/service/`
|
|
3. **Handler** — Add handler in `internal/handler/handler.go`
|
|
4. **Routes** — Register in `handler.RegisterRoutes()`
|
|
5. **Tests** — Add tests in `*_test.go` files
|
|
|
|
### Frontend Development
|
|
|
|
```bash
|
|
# Hot module replacement (Vite)
|
|
make dev-frontend
|
|
|
|
# Or manually:
|
|
cd web && npm run dev
|
|
```
|
|
|
|
#### Adding a New Page/Section
|
|
|
|
1. **Types** — Add to `web/src/types/index.ts`
|
|
2. **API** — Add endpoint to `web/src/services/api.ts`
|
|
3. **Hook** — Create custom hook in `web/src/hooks/`
|
|
4. **Component** — Create in `web/src/components/sections/`
|
|
5. **Register** — Import and add to `web/src/App.tsx`
|
|
|
|
### Code Generation
|
|
|
|
```bash
|
|
# Generate mocks for testing
|
|
make generate
|
|
|
|
# This runs: go generate ./...
|
|
# Uses: github.com/golang/mock/mockgen
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 Testing
|
|
|
|
### Backend Tests
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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)
|
|
|
|
```bash
|
|
# Install Playwright
|
|
cd web && npx playwright install
|
|
|
|
# Run E2E tests
|
|
cd web && npm run test:e2e
|
|
```
|
|
|
|
---
|
|
|
|
## 🔍 Code Quality
|
|
|
|
### Linting
|
|
|
|
```bash
|
|
# All linters
|
|
make lint
|
|
|
|
# Backend only
|
|
make lint-backend
|
|
# Runs: golangci-lint run ./...
|
|
|
|
# Frontend only
|
|
make lint-frontend
|
|
# Runs: eslint + prettier check
|
|
```
|
|
|
|
### Formatting
|
|
|
|
```bash
|
|
# Format all code
|
|
make fmt
|
|
|
|
# Backend
|
|
go fmt ./...
|
|
goimports -w .
|
|
|
|
# Frontend
|
|
cd web && npm run format
|
|
```
|
|
|
|
### Pre-commit Hooks
|
|
|
|
```bash
|
|
# Install pre-commit
|
|
pip install pre-commit
|
|
pre-commit install
|
|
|
|
# Run manually
|
|
pre-commit run --all-files
|
|
```
|
|
|
|
#### `.pre-commit-config.yaml`
|
|
```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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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`, not `fix_db`
|
|
|
|
---
|
|
|
|
## 🐳 Docker Development
|
|
|
|
### Build Locally
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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`:
|
|
```yaml
|
|
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**: `useApi` hook (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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```go
|
|
skillService := service.NewSkillService()
|
|
h := handler.NewHandler(skillService, ...)
|
|
```
|
|
|
|
### Adding Database Support
|
|
|
|
1. Add DB config to `config.yaml`
|
|
2. Create `internal/repository/` package
|
|
3. Implement interfaces in `service/`
|
|
4. Use `database/sql` or `sqlx` or `gorm`
|
|
5. Add migrations
|
|
|
|
### Middleware Chain
|
|
|
|
```go
|
|
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](https://www.conventionalcommits.org/):
|
|
|
|
```
|
|
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
|
|
|
|
1. Create feature branch from `main`
|
|
2. Make changes with tests
|
|
3. Run `make lint && make test`
|
|
4. Push and open PR
|
|
5. Require: CI passing, 1 approval
|
|
6. Squash merge to `main`
|
|
|
|
---
|
|
|
|
## 🐛 Debugging
|
|
|
|
### Backend Debugging
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# Add dependency
|
|
go get github.com/new/package
|
|
|
|
# Update all
|
|
go get -u ./...
|
|
go mod tidy
|
|
|
|
# Check outdated
|
|
make check-deps
|
|
```
|
|
|
|
### Frontend Dependencies
|
|
|
|
```bash
|
|
# 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 `pprof` for 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.memo` for 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
|
|
|
|
- [Go Project Layout](https://github.com/golang-standards/project-layout)
|
|
- [Effective Go](https://go.dev/doc/effective_go)
|
|
- [React Beta Docs](https://react.dev)
|
|
- [Tailwind CSS Docs](https://tailwindcss.com/docs)
|
|
- [Gin Framework](https://gin-gonic.com/docs/)
|
|
- [Zerolog](https://github.com/rs/zerolog)
|
|
- [Vite Guide](https://vitejs.dev/guide/)
|
|
|
|
---
|
|
|
|
## ❓ 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 |