docs: introduce comprehensive coding standards for the zhealth monorepo.
This commit is contained in:
189
PROJECT_MGMT/coding_standards.md
Normal file
189
PROJECT_MGMT/coding_standards.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# Coding Standards: zhealth Monorepo
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Modularity First** — Every component should be independent and reusable.
|
||||
2. **Self-Documenting Structure** — The folder tree and file names should explain purpose without needing comments.
|
||||
3. **Small Files** — Max **200 lines** per file. A function belongs in a file **only if its purpose is obvious from the filename**. When in doubt, create a separate file.
|
||||
|
||||
---
|
||||
|
||||
## File & Folder Naming
|
||||
|
||||
| Type | Convention | Example |
|
||||
|------|------------|---------|
|
||||
| Folders | `snake_case` | `auth_handlers/`, `biomarker_utils/` |
|
||||
| Rust files | `snake_case.rs` | `user_service.rs`, `jwt_middleware.rs` |
|
||||
| TypeScript/React files | `PascalCase.tsx` for components | `Dashboard.tsx`, `BiomarkerCard.tsx` |
|
||||
| TypeScript utilities | `camelCase.ts` | `apiClient.ts`, `formatDate.ts` |
|
||||
| Test files | `*.test.ts` / `*_test.rs` | `auth.test.ts`, `user_service_test.rs` |
|
||||
| Config files | `lowercase` with dots | `vite.config.ts`, `.env.local` |
|
||||
|
||||
---
|
||||
|
||||
## Folder Structure (Monorepo)
|
||||
|
||||
```
|
||||
zhealth/
|
||||
├── PROJECT_MGMT/ # Project planning & docs
|
||||
├── backend/ # Rust API
|
||||
│ ├── src/
|
||||
│ │ ├── handlers/ # Route handlers (one file per resource)
|
||||
│ │ ├── models/ # SeaORM entities
|
||||
│ │ ├── services/ # Business logic
|
||||
│ │ ├── middleware/ # Auth, logging, etc.
|
||||
│ │ ├── utils/ # Shared helpers
|
||||
│ │ └── main.rs
|
||||
│ └── Cargo.toml
|
||||
├── frontend/ # React + Vite
|
||||
│ ├── src/
|
||||
│ │ ├── components/ # Reusable UI components
|
||||
│ │ ├── pages/ # Route-level components
|
||||
│ │ ├── hooks/ # Custom React hooks
|
||||
│ │ ├── services/ # API calls
|
||||
│ │ ├── utils/ # Helper functions
|
||||
│ │ └── App.tsx
|
||||
│ └── package.json
|
||||
└── shared/ # Shared types/constants (if needed)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Makefile as Command Runner
|
||||
|
||||
All common tasks are executed through the root **Makefile**. Do not use raw `cargo`, `npm`, or tool-specific commands directly — always use `make`.
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `make lint` | Run linters (Clippy + ESLint) |
|
||||
| `make typecheck` | Type checking (Rust + TypeScript) |
|
||||
| `make dev` | Start dev servers (backend + frontend) |
|
||||
| `make build` | Build for development |
|
||||
| `make release` | Build optimized production bundle |
|
||||
| `make serve` | Serve production build locally |
|
||||
| `make test` | Run all tests |
|
||||
| `make clean` | Clean build artifacts |
|
||||
|
||||
### Why Makefile?
|
||||
- **Single source of truth** for all commands
|
||||
- **Works across all platforms** (unlike npm scripts for Rust)
|
||||
- **Self-documenting** — run `make help` to see available targets
|
||||
- **Composable** — targets can depend on each other
|
||||
|
||||
---
|
||||
|
||||
## Configuration (`config.yaml`)
|
||||
|
||||
All backend configuration is centralized in a **`config.yaml`** file at the backend root. Never hardcode paths, ports, or environment-specific values.
|
||||
|
||||
### Required Config Keys
|
||||
|
||||
```yaml
|
||||
# backend/config.yaml
|
||||
|
||||
server:
|
||||
host: "0.0.0.0"
|
||||
port: 3000
|
||||
|
||||
paths:
|
||||
database: "./data/zhealth.db"
|
||||
logs: "./logs"
|
||||
|
||||
auth:
|
||||
jwt_secret: "${JWT_SECRET}" # Loaded from env
|
||||
token_expiry_hours: 24
|
||||
|
||||
ai:
|
||||
provider: "gemini" # gemini | openai | anthropic
|
||||
api_key: "${AI_API_KEY}"
|
||||
```
|
||||
|
||||
### Rules
|
||||
- **Secrets via env vars** — Use `${VAR_NAME}` syntax for sensitive values
|
||||
- **Paths are relative** to the backend folder (or absolute if needed)
|
||||
- **No hardcoding** — If it might change across environments, put it in config
|
||||
|
||||
---
|
||||
|
||||
## File Size Rules
|
||||
|
||||
| Rule | Action |
|
||||
|------|--------|
|
||||
| File > 200 lines | Split into smaller modules |
|
||||
| Function > 50 lines | Extract to helper or break into sub-functions |
|
||||
| Single responsibility | One file = one purpose |
|
||||
|
||||
### Example: Splitting a Large Handler
|
||||
|
||||
❌ **Bad**: `handlers/biomarkers.rs` with 400 lines
|
||||
|
||||
✅ **Good**:
|
||||
```
|
||||
handlers/
|
||||
├── biomarkers/
|
||||
│ ├── mod.rs # Re-exports
|
||||
│ ├── create.rs # POST /biomarkers
|
||||
│ ├── read.rs # GET /biomarkers
|
||||
│ ├── update.rs # PUT /biomarkers/:id
|
||||
│ └── delete.rs # DELETE /biomarkers/:id
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Import & Module Organization
|
||||
|
||||
### Rust
|
||||
```rust
|
||||
// 1. Standard library
|
||||
use std::collections::HashMap;
|
||||
|
||||
// 2. External crates
|
||||
use axum::{Router, Json};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// 3. Internal modules
|
||||
use crate::models::User;
|
||||
use crate::services::auth_service;
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
```typescript
|
||||
// 1. React/Framework
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
// 2. External packages
|
||||
import axios from 'axios';
|
||||
|
||||
// 3. Internal components/utils
|
||||
import { BiomarkerCard } from '@/components/BiomarkerCard';
|
||||
import { formatDate } from '@/utils/formatDate';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commit & Branch Naming
|
||||
|
||||
| Type | Format | Example |
|
||||
|------|--------|---------|
|
||||
| Branch | `type/short-description` | `feat/biomarker-crud`, `fix/auth-token-expiry` |
|
||||
| Commit | `type: short description` | `feat: add biomarker create endpoint` |
|
||||
|
||||
**Types**: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`
|
||||
|
||||
---
|
||||
|
||||
## Code Comments
|
||||
|
||||
- **Avoid obvious comments** — Code should be self-explanatory.
|
||||
- **Document "why", not "what"** — Explain non-obvious decisions.
|
||||
- **Use doc comments** for public APIs:
|
||||
- Rust: `///` for functions, `//!` for modules
|
||||
- TypeScript: JSDoc `/** */` for exported functions
|
||||
|
||||
---
|
||||
|
||||
## Enforcement
|
||||
|
||||
- [ ] Pre-commit hooks for linting (Clippy for Rust, ESLint for TS)
|
||||
- [ ] CI pipeline checks for file size violations
|
||||
- [ ] PR reviews must verify modularity standards
|
||||
Reference in New Issue
Block a user