You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
knowfoolery/docs/4_work_plan/1.1-development-environment...

32 KiB

1.1 Development Environment Setup - Detailed Implementation Plan

Overview

Attribute Value
Priority CRITICAL
Duration 2-3 days
Dependencies None (first step)
Team Size 1 developer

Objectives

  1. Establish a reproducible development environment for all team members
  2. Configure Go multi-module monorepo structure
  3. Set up frontend workspace with proper tooling
  4. Create containerized local infrastructure (PostgreSQL, Redis, Zitadel)
  5. Implement consistent linting and formatting standards
  6. Document onboarding process

Task Breakdown

Task 1: Initialize Go Workspace (go.work)

Duration: 2-3 hours

1.1.1 Create Root go.work File

Create the Go workspace file at repository root to manage multiple modules.

File: /go.work

go 1.25

use (
    ./backend/services/game-session-service
    ./backend/services/question-bank-service
    ./backend/services/user-service
    ./backend/services/leaderboard-service
    ./backend/services/admin-service
    ./backend/services/gateway-service
    ./backend/shared
)

1.1.2 Initialize Each Service Module

For each service, create the go.mod file:

Directory Structure to Create:

backend/
├── go.work
├── services/
│   ├── game-session-service/
│   │   ├── go.mod
│   │   ├── go.sum
│   │   └── cmd/
│   │       └── main.go
│   ├── question-bank-service/
│   │   ├── go.mod
│   │   ├── go.sum
│   │   └── cmd/
│   │       └── main.go
│   ├── user-service/
│   │   ├── go.mod
│   │   ├── go.sum
│   │   └── cmd/
│   │       └── main.go
│   ├── leaderboard-service/
│   │   ├── go.mod
│   │   ├── go.sum
│   │   └── cmd/
│   │       └── main.go
│   ├── admin-service/
│   │   ├── go.mod
│   │   ├── go.sum
│   │   └── cmd/
│   │       └── main.go
│   └── gateway-service/
│       ├── go.mod
│       ├── go.sum
│       └── cmd/
│           └── main.go
└── shared/
    ├── go.mod
    ├── go.sum
    └── .gitkeep

Example go.mod for game-session-service:

module github.com/knowfoolery/backend/services/game-session-service

go 1.25

require (
    github.com/gofiber/fiber/v3 v3.0.0
    github.com/knowfoolery/backend/shared v0.0.0
)

replace github.com/knowfoolery/backend/shared => ../../shared

Example go.mod for shared package:

module github.com/knowfoolery/backend/shared

go 1.25

require (
    github.com/rs/zerolog v1.31.0
    github.com/go-playground/validator/v10 v10.16.0
    github.com/prometheus/client_golang v1.17.0
    go.opentelemetry.io/otel v1.21.0
)

1.1.3 Create Stub Main Files

File: backend/services/game-session-service/cmd/main.go

package main

import (
    "log"
    "github.com/gofiber/fiber/v3"
)

func main() {
    app := fiber.New(fiber.Config{
        AppName: "Know Foolery - Game Session Service",
    })

    app.Get("/health", func(c fiber.Ctx) error {
        return c.JSON(fiber.Map{"status": "healthy", "service": "game-session"})
    })

    log.Fatal(app.Listen(":8080"))
}

Repeat for each service with appropriate port:

Service Port
game-session-service 8080
question-bank-service 8081
user-service 8082
leaderboard-service 8083
admin-service 8085
gateway-service 8086

Verification Steps:

cd backend
go work sync
go build ./...

Task 2: Set Up Shared Packages Structure

Duration: 2-3 hours

1.2.1 Create Shared Package Directory Structure

backend/shared/
├── go.mod
├── go.sum
├── domain/
│   ├── errors/
│   │   ├── errors.go          # Base error types
│   │   └── codes.go           # Error code constants
│   ├── events/
│   │   ├── event.go           # Event interface
│   │   └── contracts.go       # Event type definitions
│   ├── types/
│   │   ├── id.go              # ID type wrappers
│   │   ├── pagination.go      # Pagination types
│   │   └── enums.go           # Common enums
│   └── valueobjects/
│       ├── player_name.go     # PlayerName value object
│       └── score.go           # Score value object
└── infra/
    ├── auth/
    │   ├── zitadel/
    │   │   ├── client.go      # Zitadel client
    │   │   └── middleware.go  # JWT middleware
    │   └── rbac/
    │       └── roles.go       # Role definitions
    ├── database/
    │   ├── postgres/
    │   │   └── client.go      # PostgreSQL connection factory
    │   └── redis/
    │       └── client.go      # Redis connection factory
    ├── observability/
    │   ├── logging/
    │   │   └── logger.go      # Zerolog setup
    │   ├── tracing/
    │   │   └── tracer.go      # OpenTelemetry setup
    │   └── metrics/
    │       └── prometheus.go  # Prometheus registration
    ├── security/
    │   └── sanitize.go        # Input sanitization
    └── utils/
        ├── httputil/
        │   ├── errors.go      # HTTP error responses
        │   ├── response.go    # Standard response format
        │   └── pagination.go  # Pagination helpers
        └── validation/
            └── validator.go   # Validation helpers

1.2.2 Create Placeholder Files

Create minimal placeholder implementations for each file to establish the structure.

File: backend/shared/domain/errors/errors.go

package errors

import "fmt"

// DomainError represents a domain-level error
type DomainError struct {
    Code    string
    Message string
    Err     error
}

func (e *DomainError) Error() string {
    if e.Err != nil {
        return fmt.Sprintf("[%s] %s: %v", e.Code, e.Message, e.Err)
    }
    return fmt.Sprintf("[%s] %s", e.Code, e.Message)
}

func (e *DomainError) Unwrap() error {
    return e.Err
}

// Common domain errors
var (
    ErrNotFound       = &DomainError{Code: "NOT_FOUND", Message: "Resource not found"}
    ErrValidation     = &DomainError{Code: "VALIDATION", Message: "Validation failed"}
    ErrUnauthorized   = &DomainError{Code: "UNAUTHORIZED", Message: "Unauthorized access"}
    ErrConflict       = &DomainError{Code: "CONFLICT", Message: "Resource conflict"}
    ErrInternalServer = &DomainError{Code: "INTERNAL", Message: "Internal server error"}
)

File: backend/shared/infra/observability/logging/logger.go

package logging

import (
    "os"
    "time"

    "github.com/rs/zerolog"
)

// NewLogger creates a new zerolog logger
func NewLogger(serviceName, environment string) zerolog.Logger {
    zerolog.TimeFieldFormat = time.RFC3339Nano

    var logger zerolog.Logger

    if environment == "development" {
        logger = zerolog.New(zerolog.ConsoleWriter{
            Out:        os.Stdout,
            TimeFormat: "15:04:05",
        }).With().Timestamp().Logger()
    } else {
        logger = zerolog.New(os.Stdout).With().Timestamp().Logger()
    }

    return logger.With().
        Str("service", serviceName).
        Str("environment", environment).
        Logger()
}

Verification Steps:

cd backend/shared
go mod tidy
go build ./...

Task 3: Configure Linting (golangci-lint, ESLint)

Duration: 2-3 hours

1.3.1 Backend Linting (golangci-lint)

File: backend/.golangci.yml

run:
  timeout: 5m
  tests: true
  go: "1.25"

linters:
  enable:
    - gofmt
    - goimports
    - govet
    - errcheck
    - staticcheck
    - unused
    - gosimple
    - ineffassign
    - typecheck
    - gosec
    - misspell
    - lll
    - unconvert
    - dupl
    - goconst
    - gocyclo
    - prealloc
    - bodyclose
    - noctx
    - exhaustive

linters-settings:
  lll:
    line-length: 120
  gocyclo:
    min-complexity: 15
  dupl:
    threshold: 150
  goconst:
    min-len: 3
    min-occurrences: 3
  misspell:
    locale: US
  gosec:
    excludes:
      - G104  # Unhandled errors (we use errcheck)

issues:
  exclude-rules:
    - path: _test\.go
      linters:
        - gocyclo
        - errcheck
        - dupl
        - gosec
    - path: cmd/main\.go
      linters:
        - gocyclo
  max-issues-per-linter: 50
  max-same-issues: 10

1.3.2 Frontend Linting (ESLint)

File: frontend/.eslintrc.json

{
  "root": true,
  "env": {
    "browser": true,
    "es2022": true,
    "node": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:solid/recommended",
    "prettier"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "plugins": [
    "@typescript-eslint",
    "solid"
  ],
  "rules": {
    "@typescript-eslint/explicit-function-return-type": "warn",
    "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
    "@typescript-eslint/no-explicit-any": "warn",
    "prefer-const": "error",
    "no-var": "error",
    "object-shorthand": "error",
    "prefer-template": "error",
    "no-console": ["warn", { "allow": ["warn", "error"] }]
  },
  "ignorePatterns": [
    "dist",
    "node_modules",
    "*.config.js",
    "*.config.ts"
  ]
}

File: frontend/.prettierrc

{
  "semi": false,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 100,
  "bracketSpacing": true
}

1.3.3 Editor Configuration

File: .editorconfig (repository root)

root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.go]
indent_style = tab
indent_size = 4

[*.md]
trim_trailing_whitespace = false

[Makefile]
indent_style = tab

Verification Steps:

# Backend
cd backend
golangci-lint run ./...

# Frontend
cd frontend
npx eslint . --ext .ts,.tsx
npx prettier --check .

Task 4: Create Docker Compose for Local Development

Duration: 3-4 hours

1.4.1 Development Docker Compose

File: infrastructure/dev/docker-compose.yml

version: "3.9"

services:
  # PostgreSQL Database
  postgres:
    image: postgres:15-alpine
    container_name: knowfoolery-postgres
    environment:
      POSTGRES_USER: knowfoolery
      POSTGRES_PASSWORD: devpassword
      POSTGRES_DB: knowfoolery
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init-scripts:/docker-entrypoint-initdb.d
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U knowfoolery -d knowfoolery"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - knowfoolery-network

  # Redis Cache
  redis:
    image: redis:7-alpine
    container_name: knowfoolery-redis
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - knowfoolery-network

  # Zitadel Authentication (optional for initial setup)
  zitadel:
    image: ghcr.io/zitadel/zitadel:latest
    container_name: knowfoolery-zitadel
    command: start-from-init --masterkeyFromEnv --tlsMode disabled
    environment:
      ZITADEL_MASTERKEY: "MasterkeyNeedsToHave32Characters"
      ZITADEL_DATABASE_COCKROACH_HOST: crdb
      ZITADEL_EXTERNALSECURE: false
      ZITADEL_FIRSTINSTANCE_ORG_NAME: "KnowFoolery"
      ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: "admin"
      ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: "AdminPassword123!"
    ports:
      - "8080:8080"
    depends_on:
      crdb:
        condition: service_healthy
    networks:
      - knowfoolery-network
    profiles:
      - auth

  # CockroachDB for Zitadel
  crdb:
    image: cockroachdb/cockroach:latest
    container_name: knowfoolery-crdb
    command: start-single-node --insecure --http-addr :9090
    ports:
      - "26257:26257"
      - "9090:9090"
    volumes:
      - crdb_data:/cockroach/cockroach-data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9090/health?ready=1"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - knowfoolery-network
    profiles:
      - auth

  # Prometheus Metrics
  prometheus:
    image: prom/prometheus:latest
    container_name: knowfoolery-prometheus
    ports:
      - "9091:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.enable-lifecycle'
    networks:
      - knowfoolery-network
    profiles:
      - observability

  # Grafana Dashboards
  grafana:
    image: grafana/grafana:latest
    container_name: knowfoolery-grafana
    ports:
      - "3001:3000"
    environment:
      GF_SECURITY_ADMIN_USER: admin
      GF_SECURITY_ADMIN_PASSWORD: admin
      GF_USERS_ALLOW_SIGN_UP: false
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    depends_on:
      - prometheus
    networks:
      - knowfoolery-network
    profiles:
      - observability

  # Jaeger Tracing
  jaeger:
    image: jaegertracing/all-in-one:latest
    container_name: knowfoolery-jaeger
    ports:
      - "16686:16686"  # UI
      - "14268:14268"  # Collector HTTP
      - "6831:6831/udp"  # Agent
    environment:
      COLLECTOR_ZIPKIN_HOST_PORT: ":9411"
    networks:
      - knowfoolery-network
    profiles:
      - observability

volumes:
  postgres_data:
  redis_data:
  crdb_data:
  prometheus_data:
  grafana_data:

networks:
  knowfoolery-network:
    driver: bridge

1.4.2 Database Initialization Script

File: infrastructure/dev/init-scripts/01-create-databases.sql

-- Create separate databases for each service
CREATE DATABASE IF NOT EXISTS game_sessions;
CREATE DATABASE IF NOT EXISTS questions;
CREATE DATABASE IF NOT EXISTS users;
CREATE DATABASE IF NOT EXISTS leaderboards;
CREATE DATABASE IF NOT EXISTS admin;

-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE game_sessions TO knowfoolery;
GRANT ALL PRIVILEGES ON DATABASE questions TO knowfoolery;
GRANT ALL PRIVILEGES ON DATABASE users TO knowfoolery;
GRANT ALL PRIVILEGES ON DATABASE leaderboards TO knowfoolery;
GRANT ALL PRIVILEGES ON DATABASE admin TO knowfoolery;

1.4.3 Prometheus Configuration

File: infrastructure/dev/prometheus/prometheus.yml

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'game-session-service'
    static_configs:
      - targets: ['host.docker.internal:8080']
    metrics_path: /metrics

  - job_name: 'question-bank-service'
    static_configs:
      - targets: ['host.docker.internal:8081']
    metrics_path: /metrics

  - job_name: 'user-service'
    static_configs:
      - targets: ['host.docker.internal:8082']
    metrics_path: /metrics

  - job_name: 'leaderboard-service'
    static_configs:
      - targets: ['host.docker.internal:8083']
    metrics_path: /metrics

  - job_name: 'gateway-service'
    static_configs:
      - targets: ['host.docker.internal:8086']
    metrics_path: /metrics

1.4.4 Environment Variables

File: infrastructure/dev/.env

# Database
POSTGRES_USER=knowfoolery
POSTGRES_PASSWORD=devpassword
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=knowfoolery

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379

# Zitadel (when auth profile is enabled)
ZITADEL_URL=http://localhost:8080
ZITADEL_PROJECT_ID=your-project-id
ZITADEL_CLIENT_ID=your-client-id

# Environment
ENVIRONMENT=development
LOG_LEVEL=debug

# Service Ports
GAME_SESSION_PORT=8080
QUESTION_BANK_PORT=8081
USER_SERVICE_PORT=8082
LEADERBOARD_PORT=8083
ADMIN_SERVICE_PORT=8085
GATEWAY_PORT=8086

Verification Steps:

cd infrastructure/dev
docker-compose up -d postgres redis
docker-compose ps
docker-compose logs postgres

Task 5: Initialize Frontend Workspace (Yarn Workspaces)

Duration: 2-3 hours

1.5.1 Root Package Configuration

File: frontend/package.json

{
  "name": "knowfoolery-frontend",
  "private": true,
  "workspaces": [
    "shared/*",
    "apps/*"
  ],
  "scripts": {
    "dev": "yarn workspace @knowfoolery/web dev",
    "build": "yarn workspaces foreach -A run build",
    "lint": "eslint . --ext .ts,.tsx",
    "lint:fix": "eslint . --ext .ts,.tsx --fix",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "test": "yarn workspaces foreach -A run test",
    "clean": "yarn workspaces foreach -A run clean"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^6.0.0",
    "@typescript-eslint/parser": "^6.0.0",
    "eslint": "^8.0.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-solid": "^0.13.0",
    "prettier": "^3.0.0",
    "typescript": "^5.0.0"
  },
  "packageManager": "yarn@4.0.0"
}

1.5.2 Web Application Package

File: frontend/apps/web/package.json

{
  "name": "@knowfoolery/web",
  "private": true,
  "version": "0.0.1",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "test": "vitest",
    "clean": "rm -rf dist node_modules/.vite"
  },
  "dependencies": {
    "solid-js": "^1.9.0",
    "@solidjs/router": "^0.10.0",
    "@suid/material": "^0.16.0",
    "@suid/icons-material": "^0.7.0"
  },
  "devDependencies": {
    "vite": "^5.0.0",
    "vite-plugin-solid": "^2.8.0",
    "vitest": "^1.0.0",
    "@solidjs/testing-library": "^0.8.0"
  }
}

File: frontend/apps/web/vite.config.ts

import { defineConfig } from 'vite'
import solid from 'vite-plugin-solid'

export default defineConfig({
  plugins: [solid()],
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://localhost:8086',
        changeOrigin: true,
      },
    },
  },
  build: {
    target: 'esnext',
  },
})

File: frontend/apps/web/tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "jsx": "preserve",
    "jsxImportSource": "solid-js",
    "types": ["vite/client"],
    "noEmit": true,
    "isolatedModules": true,
    "strict": true,
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@shared/*": ["../../shared/*"]
    }
  },
  "include": ["src"]
}

File: frontend/apps/web/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Know Foolery - Quiz Game</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

File: frontend/apps/web/src/main.tsx

import { render } from 'solid-js/web'
import App from './App'
import './styles/global.css'

const root = document.getElementById('root')

if (!root) {
  throw new Error('Root element not found')
}

render(() => <App />, root)

File: frontend/apps/web/src/App.tsx

import type { Component } from 'solid-js'

const App: Component = () => {
  return (
    <div>
      <h1>Know Foolery</h1>
      <p>Quiz game coming soon...</p>
    </div>
  )
}

export default App

File: frontend/apps/web/src/styles/global.css

:root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  font-weight: 400;

  color-scheme: light dark;
  color: rgba(255, 255, 255, 0.87);
  background-color: #242424;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

#root {
  max-width: 1280px;
  margin: 0 auto;
  padding: 2rem;
}

1.5.3 Shared UI Components Package

File: frontend/shared/ui-components/package.json

{
  "name": "@knowfoolery/ui-components",
  "private": true,
  "version": "0.0.1",
  "type": "module",
  "main": "src/index.ts",
  "scripts": {
    "test": "vitest",
    "build": "echo 'No build needed for shared package'",
    "clean": "echo 'Nothing to clean'"
  },
  "dependencies": {
    "solid-js": "^1.9.0",
    "@suid/material": "^0.16.0"
  },
  "devDependencies": {
    "vitest": "^1.0.0"
  },
  "peerDependencies": {
    "solid-js": "^1.9.0"
  }
}

File: frontend/shared/ui-components/src/index.ts

// UI Components barrel export
// Components will be added as they are developed
export {}

Verification Steps:

cd frontend
yarn install
yarn dev
# Open http://localhost:3000

Task 6: Create Makefile for Development Commands

Duration: 1-2 hours

File: Makefile (repository root)

.PHONY: help dev dev-full dev-auth dev-obs stop clean \
        backend-lint backend-test backend-build \
        frontend-lint frontend-test frontend-build \
        db-up db-down db-logs db-shell redis-shell \
        lint test build

# Default target
help:
	@echo "KnowFoolery Development Commands"
	@echo ""
	@echo "Development Environment:"
	@echo "  make dev          - Start core infrastructure (PostgreSQL, Redis)"
	@echo "  make dev-full     - Start all infrastructure including observability"
	@echo "  make dev-auth     - Start infrastructure with Zitadel auth"
	@echo "  make stop         - Stop all containers"
	@echo "  make clean        - Stop containers and remove volumes"
	@echo ""
	@echo "Backend:"
	@echo "  make backend-lint  - Run Go linters"
	@echo "  make backend-test  - Run Go tests"
	@echo "  make backend-build - Build all services"
	@echo ""
	@echo "Frontend:"
	@echo "  make frontend-dev   - Start frontend dev server"
	@echo "  make frontend-lint  - Run ESLint and Prettier"
	@echo "  make frontend-test  - Run frontend tests"
	@echo "  make frontend-build - Build frontend for production"
	@echo ""
	@echo "Database:"
	@echo "  make db-logs      - Show PostgreSQL logs"
	@echo "  make db-shell     - Open PostgreSQL shell"
	@echo "  make redis-shell  - Open Redis CLI"
	@echo ""
	@echo "All:"
	@echo "  make lint         - Run all linters"
	@echo "  make test         - Run all tests"
	@echo "  make build        - Build everything"

# =============================================================================
# Development Environment
# =============================================================================

dev:
	@echo "Starting core infrastructure..."
	cd infrastructure/dev && docker-compose up -d postgres redis
	@echo "Waiting for services to be healthy..."
	@sleep 5
	@echo ""
	@echo "Infrastructure ready:"
	@echo "  PostgreSQL: localhost:5432 (user: knowfoolery, password: devpassword)"
	@echo "  Redis:      localhost:6379"

dev-full: dev
	@echo "Starting observability stack..."
	cd infrastructure/dev && docker-compose --profile observability up -d
	@echo ""
	@echo "Observability ready:"
	@echo "  Prometheus: http://localhost:9091"
	@echo "  Grafana:    http://localhost:3001 (admin/admin)"
	@echo "  Jaeger:     http://localhost:16686"

dev-auth:
	@echo "Starting full stack with authentication..."
	cd infrastructure/dev && docker-compose --profile auth up -d
	@echo ""
	@echo "Zitadel ready at http://localhost:8080"
	@echo "Admin credentials: admin / AdminPassword123!"

stop:
	@echo "Stopping all containers..."
	cd infrastructure/dev && docker-compose --profile auth --profile observability down

clean:
	@echo "Stopping containers and removing volumes..."
	cd infrastructure/dev && docker-compose --profile auth --profile observability down -v

# =============================================================================
# Backend
# =============================================================================

backend-lint:
	@echo "Running Go linters..."
	cd backend && golangci-lint run ./...

backend-test:
	@echo "Running Go tests..."
	cd backend && go test -v -race -cover ./...

backend-build:
	@echo "Building all services..."
	cd backend && go build ./...

# =============================================================================
# Frontend
# =============================================================================

frontend-dev:
	@echo "Starting frontend development server..."
	cd frontend && yarn dev

frontend-lint:
	@echo "Running frontend linters..."
	cd frontend && yarn lint && yarn format:check

frontend-test:
	@echo "Running frontend tests..."
	cd frontend && yarn test

frontend-build:
	@echo "Building frontend..."
	cd frontend && yarn build

# =============================================================================
# Database Utilities
# =============================================================================

db-logs:
	cd infrastructure/dev && docker-compose logs -f postgres

db-shell:
	docker exec -it knowfoolery-postgres psql -U knowfoolery -d knowfoolery

redis-shell:
	docker exec -it knowfoolery-redis redis-cli

# =============================================================================
# Combined Commands
# =============================================================================

lint: backend-lint frontend-lint
	@echo "All linters passed!"

test: backend-test frontend-test
	@echo "All tests passed!"

build: backend-build frontend-build
	@echo "Build complete!"

Task 7: Create Developer Onboarding Documentation

Duration: 1-2 hours

File: docs/DEVELOPMENT.md

# KnowFoolery Development Guide

## Prerequisites

Before you begin, ensure you have the following installed:

| Tool | Version | Installation |
|------|---------|--------------|
| Go | 1.25+ | https://go.dev/dl/ |
| Node.js | 20+ | https://nodejs.org/ or use nvm |
| Yarn | 4.x | `corepack enable` |
| Docker | 24+ | https://docs.docker.com/get-docker/ |
| Docker Compose | 2.x | Included with Docker Desktop |
| golangci-lint | latest | `go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest` |

## Quick Start

### 1. Clone the Repository

```bash
git clone https://github.com/knowfoolery/knowfoolery.git
cd knowfoolery

2. Start Infrastructure

# Start PostgreSQL and Redis
make dev

# Or start everything including observability
make dev-full

3. Install Dependencies

# Backend
cd backend
go mod download

# Frontend
cd frontend
yarn install

4. Start Development Servers

Backend Services (run each in a separate terminal):

cd backend/services/game-session-service && go run cmd/main.go
cd backend/services/question-bank-service && go run cmd/main.go
# ... repeat for other services

Frontend:

cd frontend
yarn dev

5. Access the Application

Project Structure

knowfoolery/
├── backend/
│   ├── go.work          # Go workspace file
│   ├── shared/          # Shared packages
│   └── services/        # Microservices
├── frontend/
│   ├── package.json     # Yarn workspace root
│   ├── apps/            # Applications (web, cross-platform)
│   └── shared/          # Shared components
├── infrastructure/
│   └── dev/             # Docker Compose for local dev
├── docs/                # Documentation
└── Makefile             # Development commands

Common Commands

Command Description
make help Show all available commands
make dev Start core infrastructure
make dev-full Start infrastructure + observability
make lint Run all linters
make test Run all tests
make stop Stop all containers
make clean Stop containers and remove data

Environment Variables

Copy the example environment file:

cp infrastructure/dev/.env.example infrastructure/dev/.env

Database Access

PostgreSQL

# Via make command
make db-shell

# Or directly
docker exec -it knowfoolery-postgres psql -U knowfoolery -d knowfoolery

Redis

# Via make command
make redis-shell

# Or directly
docker exec -it knowfoolery-redis redis-cli

Troubleshooting

Port Conflicts

If you get port conflict errors:

  1. Check what's using the port: lsof -i :5432
  2. Stop the conflicting service or change ports in docker-compose.yml

Docker Memory Issues

If containers are slow or crashing:

  1. Increase Docker Desktop memory allocation (Settings > Resources)
  2. Recommended: 4GB+ RAM

Go Module Issues

cd backend
go work sync
go mod tidy

Frontend Build Issues

cd frontend
rm -rf node_modules
yarn cache clean
yarn install

IDE Setup

VS Code

Recommended extensions:

  • Go (golang.go)
  • ESLint
  • Prettier
  • Docker
  • YAML

GoLand/IntelliJ

  1. Open the backend folder as your Go project
  2. Enable "Go Modules integration"
  3. Set GOROOT to your Go 1.25+ installation

Contributing

  1. Create a feature branch: git checkout -b feature/my-feature
  2. Make your changes
  3. Run linters: make lint
  4. Run tests: make test
  5. Commit with conventional commits: git commit -m "feat: add new feature"
  6. Push and create a PR

---

## Deliverables Checklist

| Deliverable | Status | Notes |
|-------------|--------|-------|
| Go workspace (go.work) | [ ] | Multi-module monorepo |
| Service module stubs | [ ] | 6 services with health endpoints |
| Shared packages structure | [ ] | domain/ and infra/ directories |
| golangci-lint config | [ ] | .golangci.yml |
| ESLint + Prettier config | [ ] | .eslintrc.json, .prettierrc |
| EditorConfig | [ ] | .editorconfig |
| Docker Compose (dev) | [ ] | PostgreSQL, Redis, optional stacks |
| Makefile | [ ] | Development commands |
| Developer documentation | [ ] | DEVELOPMENT.md |

---

## Verification Checklist

### Backend Verification
```bash
# 1. Go workspace builds
cd backend && go build ./...

# 2. Linter runs without config errors
cd backend && golangci-lint run ./...

# 3. All modules resolve correctly
cd backend && go work sync

Frontend Verification

# 1. Dependencies install
cd frontend && yarn install

# 2. Dev server starts
cd frontend && yarn dev

# 3. Linters pass
cd frontend && yarn lint

Infrastructure Verification

# 1. Docker Compose starts
cd infrastructure/dev && docker-compose up -d postgres redis

# 2. PostgreSQL is accessible
docker exec knowfoolery-postgres pg_isready -U knowfoolery

# 3. Redis is accessible
docker exec knowfoolery-redis redis-cli ping

Estimated Time Breakdown

Task Duration Cumulative
Task 1: Go Workspace 2-3 hours 3 hours
Task 2: Shared Packages 2-3 hours 6 hours
Task 3: Linting Config 2-3 hours 9 hours
Task 4: Docker Compose 3-4 hours 13 hours
Task 5: Frontend Workspace 2-3 hours 16 hours
Task 6: Makefile 1-2 hours 18 hours
Task 7: Documentation 1-2 hours 20 hours
Buffer & Testing 4 hours 24 hours (3 days)

Next Steps After Completion

Once this phase is complete, proceed to:

  • Phase 1.2: Implement shared backend packages (errors, logging, metrics, tracing)
  • Begin development of Question Bank Service (Phase 2.1)