Skip to main content

Development Guide

This guide covers coding conventions, project structure, and development workflows.

Project Structure​

tutornexus/
├── apps/
│ ├── api/ # Cloudflare Workers API (Hono + OpenAPI)
│ └── web/ # React frontend
├── services/
│ ├── mcp/ # Go MCP server
│ └── adapters/ # TypeScript adapter Workers
├── tools/
│ └── cli/ # Rust CLI
├── packages/ # Shared TypeScript packages
├── docs/ # Docusaurus documentation
├── d1/ # Database migrations
└── scripts/ # Utility scripts

Coding Conventions​

TypeScript​

// Use explicit types for function parameters and returns
function getUserById(id: string): User | null {
return db.users.find(u => u.id === id);
}

// Use interfaces for object shapes
interface Session {
id: string;
userId: string;
title: string;
status: SessionStatus;
createdAt: Date;
updatedAt: Date;
}

// Prefer const assertions for readonly data
const ADAPTERS = ['sfu', 'bcit', 'ubc'] as const;

Go​

// Use error handling consistently
func (s *Server) HandleRequest(ctx context.Context, req Request) (*Response, error) {
if err := req.Validate(); err != nil {
return nil, fmt.Errorf("validation failed: %w", err)
}

result, err := s.service.Process(ctx, req)
if err != nil {
return nil, err
}

return &Response{Data: result}, nil
}

// Use structured logging
log.Info("processing request",
zap.String("request_id", req.ID),
zap.String("user_id", req.UserID),
)

Rust​

// Use Result types for fallible operations
fn parse_config(path: &Path) -> Result<Config, ConfigError> {
let file = File::open(path)
.context("failed to open config file")?;

let config: Config = serde_yaml::from_reader(file)
.context("failed to parse config YAML")?;

Ok(config)
}

// Use anyhow for application errors
use anyhow::{Context, Result, bail};

Git Workflow​

Commit Messages​

We follow Conventional Commits:

<type>(<scope>): <description>

[optional body]

[optional footer]

Types:

  • feat - New feature
  • fix - Bug fix
  • docs - Documentation changes
  • style - Code style (formatting, no logic)
  • refactor - Code restructuring
  • perf - Performance improvements
  • test - Adding or modifying tests
  • chore - Build, CI, tooling
  • breaking - Breaking change

Examples:

feat(api): add tn_transfer_resolve tool
fix(mcp): handle missing course references
docs(adr): update ADR-012 with error conventions
refactor(cli): simplify REPL input handling

Branch Naming​

feature/<issue-number>-<short-description>
bugfix/<issue-number>-<short-description>
docs/<short-description>
chore/<short-description>

Pull Requests​

  1. Create a feature branch from main
  2. Make changes following conventions
  3. Run linting and tests locally
  4. Open PR with description
  5. Get required approvals
  6. Squash and merge

Testing​

TypeScript Tests​

# Run all tests
pnpm test

# Run with coverage
pnpm test:coverage

# Watch mode
pnpm test:watch

Go Tests​

go test ./... -v
go test ./... -coverprofile=coverage.out

Rust Tests​

cargo test
cargo test --doc

Linting​

# TypeScript
pnpm lint
pnpm lint:fix

# Markdown
pnpm lint:markdown

# Commit messages
npx commitlint --from=main --to=HEAD

Building​

# Build all packages
pnpm build

# Build individual packages
pnpm build:api
pnpm build:web
pnpm build:cli
pnpm build:docs

Development Tools​

Database Management​

# Setup local database
pnpm db:setup

# Run migrations
pnpm db:migrate

# Push schema changes
pnpm db:push

# Open D1 console
pnpm db:console

API Documentation​

# Generate OpenAPI spec
pnpm api:generate

# Serve Swagger UI locally
pnpm api:docs

Debugging​

Workers​

# View logs
wrangler tail

# Viewtail in real-time
wrangler tail --project-name=tutor-nexus-api

Database​

# Query local database
wrangler d1 execute tutor-nexus-api --local --command="SELECT * FROM users LIMIT 10"

Best Practices​

Performance​

  • Use Cloudflare Durable Objects for real-time state
  • Cache expensive operations with KV
  • Batch database queries where possible
  • Use pagination for list endpoints

Security​

  • Never commit secrets or API keys
  • Use environment variables for credentials
  • Validate all input with Zod schemas
  • Use parameterized queries
  • Implement rate limiting

Documentation​

  • Document all public APIs
  • Write docstrings for complex functions
  • Keep ADRs up to date
  • Add code comments for non-obvious logic

See Also​