Files
bookhoard/PROJECT_GUIDELINES.md
T
john-okeefe 6562b20ee5 docs: add code indentation guideline to project standards
Add explicit guideline specifying 2-space indentation for all code files
unless the language prohibits it. This ensures consistent formatting across
the entire codebase and prevents debates about tab vs space preferences.

The guideline is placed in the General section alongside other coding
convention rules to maintain consistency in project standards.
2026-02-27 16:57:41 -05:00

19 KiB

Combined Project Guidelines for Bookhoard

🚨 CRITICAL PROHIBITIONS (Never violate these)

Backend & Database

  • NEVER modify backend code when working on frontend-only tasks
  • NEVER modify database schema unless explicitly instructed for full-stack changes
  • NEVER use Docker - use Podman only
  • NEVER build server binaries locally - all builds through Dockerfile/docker-compose
  • NEVER create new migration files - merge changes into current one until release
  • NEVER use git checkout on schema files without checking what will be lost
  • NEVER break existing functionality unless explicitly instructed
  • NEVER duplicate business logic - keep logic in services, not handlers
  • NEVER bypass service layer - all database operations must go through services

Testing

  • ALWAYS use setupTestServer() helper from cmd/server/tests/test_helpers.go
  • Share one test setup across all subtests - call setupTestServer() once at test function level, not per subtest
  • Prefer table-driven tests - use t.Run() with test cases instead of duplicate test functions
  • Configure database pools efficiently - use max_conns=1 for test pools (via pgxpool.ParseConfig()) to prevent connection exhaustion
  • NEVER create separate pgxpool per test - each pool creates 4 connections by default; 78 tests = 312 potential connections > PostgreSQL's 100 limit
  • NEVER call setupTestServer() in loops or within subtests - creates unnecessary database pools and exhausts connections
  • DO verify tests pass - run full test suite before completing work
  • Use t.Cleanup() properly - the TestServerSetup pattern automatically handles cleanup via t.Cleanup()

Frontend & Styling

  • NEVER modify backend/API for frontend features without user confirmation
  • NEVER use custom CSS - TailwindCSS classes only
    • ⚠️ EXCEPTION: templates/error.templ may have inline CSS because error pages must work when main app fails (404, server errors, CSS fails to load)
  • NEVER use JavaScript - convert all to TypeScript
    • ⚠️ EXCEPTION: Inline JS function calls in HTML attributes (e.g., onclick="myFunction()") are acceptable for simple interactions
  • NEVER use Object-Oriented Programming (no classes, inheritance, or this-capture)
  • DO use procedural/imperative style as your default
  • DO borrow functional techniques when they simplify code
  • DO avoid ideological purity - the best paradigm is the one that fits the problem
  • NEVER add new Dockerfiles without user confirmation
  • NEVER fetch initial data via AJAX on page load - use server-side rendering instead
  • NEVER break progressive enhancement - pages must work without JavaScript

Note: Go methods in the backend are fine and encouraged. This guideline applies to TypeScript/JavaScript frontend code only.**

General

  • NEVER skip pre-commit hooks unless explicitly requested
  • NEVER force push to main/master branches
  • NEVER commit files with secrets (.env, credentials.json, etc.)
  • NEVER make assumptions - ask clarifying questions when uncertain
  • NEVER delete code without reading full context first (minimum 20 lines before/after)
  • NEVER make cascading fix-up edits without git diff review
    • After compilation error: STOP, review git diff, understand full impact
    • Use revert/reapply pattern instead of blind fixes
  • NEVER skip post-edit verification - must compile after each file edit
  • NEVER run git commands concurrently - always run sequentially:
    • Run git add <files> and wait for completion
    • Run git commit -m "<message>" and wait for completion
    • Run git push and wait for completion
    • Never use && to chain git commands together
  • Make good organized git commits for the entire project (not just what you changed) and push - run git add, commit, push sequentially as separate commands, no need to repeatedly check status
  • Indentation is always 2 spaces unless the language prohibits it

Cascading Fix-up Pattern (PROHIBITED)

WHAT NOT TO DO - This caused critical bugs:

// ❌ WRONG: Blindly making fixes after compilation error

Edit 1: Delete deprecated code
[Compilation error: undefined Register]

Edit 2: Try to fix error (over-broad deletion)
[More errors: undefined Login, GetProfile, etc.]

Edit 3: Try to fix again (worse damage)
[Even more errors: major functionality broken]

CORRECT APPROACH:

// ✅ CORRECT: Stop, understand, then fix deliberately

Edit 1: Delete deprecated code
[Compilation error: undefined Register]

STOP  Review git diff  Understand Register was accidentally deleted
RESTORE  Get exact Register function from git history
VERIFY  Compile successfully

Key Principle: When compilation errors occur after edits:

  1. STOP - Don't make more edits
  2. ANALYZE - Use git diff to understand what was changed
  3. RECOVER - Restore what was accidentally deleted/broken
  4. VERIFY - Compile and test before proceeding

🎯 CONTEXT-SPECIFIC RULES

When Working on Frontend-Only Tasks

  • DO NOT touch backend code - handlers, services, database layer
  • DO NOT modify API routes - use existing endpoints only
  • DO NOT change database schema - work with existing structure
  • If backend change seems necessary:
    1. Identify the required change
    2. Explain why you need it
    3. Provide impact analysis
    4. ASK FOR USER CONFIRMATION before proceeding

When Working on Full-Stack Tasks

  • Backend changes are allowed when explicitly part of the task
  • Still follow all database protocols (atomic changes, validation, etc.)
  • If modifying database schema: Update local database after schema.sql changes (see Database Operations section)
  • Still use Podman for all builds
  • Still include Bruno tests for API changes

MANDATORY REQUIREMENTS

Database Operations (Full-Stack Tasks Only)

  • Follow pgx v5 standards for all database operations
  • Treat schema changes as ATOMIC - complete success or complete rejection
  • ⚠️ CRITICAL: This is a pre-production application (NO production deployments exist)
    • When database/schema/schema.sql is updated, local databases must be updated
    • Option 1 (Recommended): Recreate database with fresh schema:
      podman compose down -v  # Delete volumes (WARNING: loses all data)
      podman compose up -d    # Start fresh with new schema
      
    • Option 2: Manually apply schema changes to existing database using psql
    • DO NOT create migration files - no legacy schema support needed
  • Use pre-change checklist: read schema → identify columns → plan changes → verify → read back
  • Post-change validation: ensure schema.sql, models.go, and queries.sql are in sync

Build & Deployment

  • Use Podman exclusively (not Docker)
  • All builds through existing Dockerfile and docker-compose.yml
  • Stop building server binaries - everything goes through containers

API Changes (Full-Stack Tasks Only)

  • Include Bruno OpenCollection YAML requests with all API documentation
  • Tests must be comprehensive and cover three contexts: no user, user, and admin
  • Maintain backward compatibility for mobile apps and external consumers

Frontend & Styling

  • Always use TailwindCSS classes for all styling
  • Convert all JavaScript to TypeScript
  • Never use Object-Oriented Programming (no classes, inheritance, or this-capture)
  • Use procedural/imperative style as your default
  • Borrow functional techniques when they simplify code
  • Avoid ideological purity - the best paradigm is the one that fits the problem
  • Extract domain concepts/types only when clearly beneficial - apply YAGNI, avoid over-engineering
  • Render initial data server-side in Go templates for fast page loads
  • Use JavaScript/HTMX for CRUD operations (create, update, delete)
  • Ensure progressive enhancement - pages work without JavaScript

Service Layer Architecture

  • All business logic in services - never in handlers
  • Services must be reusable by both SSR handlers and API endpoints
  • Database operations through services only - never direct from handlers
  • When adding features: Add service logic → Create API endpoint → Use SSR for initial render → Use JS for updates

Code Organization

  • Minimize project structure changes
  • Place new files in contextually appropriate directories
  • Follow KISS, DRY, and YAGNI principles
  • Use multiple, logical git commits with clear messages
  • NEVER duplicate types between handlers and templates - define data types in handlers, reuse directly in templates
  • DO share handler types with templates - templates should use handlers.CollectionData, handlers.BookInfo, etc. directly
  • NEVER create parallel type systems - no templates.XxxData types for data that originates from handlers
  • DO enhance handler types with template-specific fields when needed (e.g., add Icon, FormattedDate fields to handlers structs)
  • DO keep template-only types in templates package - PageData, UnsafeHTML, and template-specific utilities are appropriate
  • NEVER create conversion helper functions to map between handler and template types - use handler types directly

Configuration & Environment

  • If .env is missing, auto-generate secure values
  • Never commit secrets to repository

Code Modification Safety

  • Post-Edit Verification (MANDATORY for ALL file modifications):
    • Run go build for affected packages immediately after each edit
    • Review git diff filename to verify only intended changes
    • Validate functionality still works as expected
    • Never proceed to next file until current edit is verified
  • Backup Before Large Changes:
    • Create a stash: git stash push -m "Pre-cleanup snapshot" before removing >50 lines
    • Or create a backup branch: git branch backup-before-cleanup
    • This allows instant recovery if mistakes occur
  • Large Deletion Safety Pattern:
    • Read at least 20 lines before/after deletion target
    • Include unique identifiers in match (function signatures, specific comments)
    • Use narrow matches - avoid generic patterns
    • Verify line numbers match intended section

Documentation

Documentation Structure (updated with full docs system):

  • README.md - Project overview, quick start, and setup instructions only
  • docs/ - Comprehensive documentation system with search
  • docs/developer/api/ - API reference documentation (split by endpoint/category)
  • docs/user/ - User-facing features, guides, and workflows
  • docs/user/devices/ - Device setup guides (KOBO, KOReader, etc.)
  • docs/contributing/ - Development and contribution guides

Where to document changes:

Change Type Location Examples
User-facing features docs/user/ New features, UI changes, workflows
API endpoints docs/developer/api/<category>/<endpoint>.md New endpoints, modified responses, authentication changes
API behavior Update existing docs/developer/api/ files Parameter changes, error codes, rate limits
Device setup docs/user/devices/ New device support, setup instructions
Development docs/contributing/ Build changes, architecture decisions
Quick start/setup README.md Installation, environment setup, first-run
Breaking changes Both README.md and relevant docs/ Migration guides, deprecation notices
Bug fixes Update relevant docs/ only if user-visible Clarifications, troubleshooting additions
Bruno OpenCollection YAML tests .yml files in bruno folder in appropriate folder/sub-folder API contract testing, examples

Documentation Update Workflow:

  1. Identify the audience (end users, developers, API consumers)
  2. Choose appropriate location based on table above
  3. Update documentation before or with code changes
  4. Verify documentation renders at /docs endpoint
  5. Test search finds new/updated content
  6. For API changes: Update both docs/developer/api/ files AND Bruno OpenCollection YAML .yml files
  7. Commit separately with clear message: docs: <description>

When in doubt:

  • End-user visible → docs/user/
  • API reference → docs/developer/api/
  • Setup/onboarding → README.md
  • Development related → docs/contributing/

Process & Continuity

  • If mid-task and receive "no response", continue the task
  • Verify no regressions before modifying/removing code

🔧 TECHNICAL STANDARDS

Backend Stack

  • Language: Go 1.25+
  • Database: PostgreSQL 15+ with pgx v5 driver only
  • Authentication: JWT tokens with bcrypt password hashing
  • Architecture: Service layer pattern (handlers → services → database)

Frontend Stack

  • Styling: TailwindCSS (no custom CSS)
  • Language: TypeScript (no JavaScript)
  • Templates: HTMX with server-side rendering
  • Patterns: Procedural/imperative with functional techniques where helpful (no OOP)

Containerization

  • Runtime: Podman (not Docker)
  • Build: Existing Dockerfile and docker-compose.yml only
  • No local builds allowed

🔄 ERROR RECOVERY PROTOCOL

When code modification mistakes occur (deleted wrong code, broke compilation, etc.):

Immediate Actions

  1. STOP - Don't make more edits
  2. ASSESS - What was deleted? Is it critical?
  3. REVIEW - Run git diff to see exact changes
  4. RESTORE - Choose appropriate recovery method:
    • Recent mistake: git checkout -- filename
    • Complex restoration: Use git show HEAD:filename to recover deleted code
    • Multiple files: git reset HEAD~1 (if safe)
  5. VERIFY - Compile and test restored code
  6. DOCUMENT - Note what went wrong for future reference

Recovery Examples

# Recover a deleted function from original file
git show HEAD:internal/handlers/auth.go | sed -n '70,275p' > recovery.txt

# Revert entire file to original state
git checkout HEAD -- internal/handlers/auth.go

# Use git stash to save current state before rollback
git stash push -m "Broken state before fix"
git checkout -- internal/handlers/auth.go

Prevention (Learn From Mistakes)

  • Why did the mistake happen?
  • Was it too-broad matching?
  • Was it insufficient context reading?
  • Was it cascading fix-up attempts?
  • Update guidelines to prevent recurrence

📋 WORKFLOW CHECKLISTS

Before Making Frontend-Only Changes

  • Identify if backend modification could make implementation simpler
  • Plan to use existing API endpoints only
  • If backend change seems necessary, prepare confirmation request:
    • Required change description
    • Why it would help
    • Impact analysis
    • Alternative approaches considered
  • Plan git commit structure (multiple logical commits)
  • Identify documentation location (see Documentation section):
    • User-facing feature → docs/user/
    • UI/workflow changes → docs/user/
    • Setup instructions → README.md

Before Making Full-Stack Changes

  • Read current schema completely (if database changes)
  • Identify all columns that must be preserved
  • Plan exact changes needed
  • Verify Podman will be used for builds
  • Plan git commit structure (multiple logical commits)
  • Identify documentation location:
    • API changes → docs/developer/api/<category>/
    • New endpoints → Create new .md file in docs/developer/api/
    • API behavior → Update existing docs/developer/api/ files
    • Breaking changes → Both README.md + relevant docs/
    • Bruno OpenCollection YAML .yml files → Update/create alongside API changes

During Schema Changes (Full-Stack Only)

  • Read current schema completely
  • Identify all columns that must be preserved
  • Plan exact changes needed
  • Set up verification step
  • Make intended changes
  • Immediately verify by reading back modified sections
  • Confirm ALL expected columns are present
  • Verify schema.sql, models.go, and queries.sql are in sync
  • Update local database (choose ONE):
    • Option 1 - Recreate database (recommended, loses data):
      podman compose down -v  # Delete all volumes
      podman compose up -d    # Start with fresh schema
      
    • Option 2 - Manual SQL migration (preserves data):
      podman exec bookhoard_db psql -U postgres -d bookhoard -c "YOUR SQL HERE"
      
  • Verify database has new schema (check column types, indexes, etc.)

After API Changes

  • Create/update Bruno OpenCollection YAML requests
  • Test with no user context
  • Test with regular user context
  • Test with admin context
  • Verify backward compatibility

Before Committing

  • Run verification script: bash scripts/verify-guidelines.sh
  • Fix any errors - verification must pass (0 errors) to commit
  • Note warnings - informational only, do not auto-fix
  • Run tests: go test ./... -v
  • Run lint/typecheck if available
  • Ensure no secrets in changes
  • Verify logical commit structure
  • Update documentation (see Documentation section):
    • User-facing changes → docs/user/
    • API changes → docs/developer/api/ + Bruno OpenCollection YAML .yml files
    • Setup/onboarding → README.md
    • Development changes → docs/contributing/
  • Verify docs render at /docs endpoint
  • Test docs search finds new content

Error Recovery Protocol (If Code Mistakes Occur)

  • Stop immediately - don't make more edits
  • Assess impact: What was deleted? Is it critical?
  • Review git diff: See exact changes made
  • Restore strategy:
    • If recent mistake: git checkout -- filename
    • If complex: Reconstruct from git diff using git show HEAD:filename
  • Verify recovery: Compile and test restored code
  • Document mistake: Note what went wrong for future reference

Phase Completion Verification (Before Declaring "Complete")

  • All target code is removed/intact as intended
  • No unintended code was deleted
  • All affected files compile successfully
  • Run go build ./... for entire project
  • Run verification script: bash scripts/verify-guidelines.sh - must pass (0 errors)
  • No critical functionality was broken
  • Git diff shows only intended changes
  • Review all modified files with git diff --stat

🏗 ARCHITECTURAL PATTERNS

Current: Hybrid SSR

Browser → Go template (with data) → Display instantly
                    ↓
          JavaScript for interactivity (CRUD)
                    ↓
               Shared service layer

Ultimately, whenever you are unsure just ask for confirmation.