5.5 KiB
5.5 KiB
Coding Standards: zhealth Monorepo
Core Principles
- Modularity First — Every component should be independent and reusable.
- Self-Documenting Structure — The folder tree and file names should explain purpose without needing comments.
- 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 helpto 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
# 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
// 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
// 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
- Rust:
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