Files
calypso/docs/alpha/CODING-STANDARDS.md
2026-01-04 13:19:40 +07:00

789 lines
16 KiB
Markdown

# Coding Standards
## AtlasOS - Calypso Backup Appliance
**Version:** 1.0.0-alpha
**Date:** 2025-01-XX
**Status:** Active
---
## 1. Overview
This document defines the coding standards and best practices for the Calypso project. All code must adhere to these standards to ensure consistency, maintainability, and quality.
## 2. General Principles
### 2.1 Code Quality
- **Readability**: Code should be self-documenting and easy to understand
- **Maintainability**: Code should be easy to modify and extend
- **Consistency**: Follow consistent patterns across the codebase
- **Simplicity**: Prefer simple solutions over complex ones
- **DRY**: Don't Repeat Yourself - avoid code duplication
### 2.2 Code Review
- All code must be reviewed before merging
- Reviewers should check for adherence to these standards
- Address review comments before merging
### 2.3 Documentation
- Document complex logic and algorithms
- Keep comments up-to-date with code changes
- Write clear commit messages
---
## 3. Backend (Go) Standards
### 3.1 Code Formatting
#### 3.1.1 Use gofmt
- Always run `gofmt` before committing
- Use `goimports` for import organization
- Configure IDE to format on save
#### 3.1.2 Line Length
- Maximum line length: 100 characters
- Break long lines for readability
#### 3.1.3 Indentation
- Use tabs for indentation (not spaces)
- Tab width: 4 spaces equivalent
### 3.2 Naming Conventions
#### 3.2.1 Packages
```go
// Good: lowercase, single word, descriptive
package storage
package auth
package monitoring
// Bad: mixed case, abbreviations
package Storage
package Auth
package Mon
```
#### 3.2.2 Functions
```go
// Good: camelCase, descriptive
func createZFSPool(name string) error
func listNetworkInterfaces() ([]Interface, error)
func validateUserInput(input string) error
// Bad: unclear names, abbreviations
func create(name string) error
func list() ([]Interface, error)
func val(input string) error
```
#### 3.2.3 Variables
```go
// Good: camelCase, descriptive
var poolName string
var networkInterfaces []Interface
var isActive bool
// Bad: single letters, unclear
var n string
var ifs []Interface
var a bool
```
#### 3.2.4 Constants
```go
// Good: PascalCase for exported, camelCase for unexported
const DefaultPort = 8080
const maxRetries = 3
// Bad: inconsistent casing
const defaultPort = 8080
const MAX_RETRIES = 3
```
#### 3.2.5 Types and Structs
```go
// Good: PascalCase, descriptive
type ZFSPool struct {
ID string
Name string
Status string
}
// Bad: unclear names
type Pool struct {
I string
N string
S string
}
```
### 3.3 File Organization
#### 3.3.1 File Structure
```go
// 1. Package declaration
package storage
// 2. Imports (standard, third-party, local)
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/atlasos/calypso/internal/common/database"
)
// 3. Constants
const (
defaultTimeout = 30 * time.Second
)
// 4. Types
type Service struct {
db *database.DB
}
// 5. Functions
func NewService(db *database.DB) *Service {
return &Service{db: db}
}
```
#### 3.3.2 File Naming
- Use lowercase with underscores: `handler.go`, `service.go`
- Test files: `handler_test.go`
- One main type per file when possible
### 3.4 Error Handling
#### 3.4.1 Error Return
```go
// Good: always return error as last value
func createPool(name string) (*Pool, error) {
if name == "" {
return nil, fmt.Errorf("pool name cannot be empty")
}
// ...
}
// Bad: panic, no error return
func createPool(name string) *Pool {
if name == "" {
panic("pool name cannot be empty")
}
}
```
#### 3.4.2 Error Wrapping
```go
// Good: wrap errors with context
if err != nil {
return fmt.Errorf("failed to create pool %s: %w", name, err)
}
// Bad: lose error context
if err != nil {
return err
}
```
#### 3.4.3 Error Messages
```go
// Good: clear, actionable error messages
return fmt.Errorf("pool '%s' already exists", name)
return fmt.Errorf("insufficient disk space: need %d bytes, have %d bytes", needed, available)
// Bad: unclear error messages
return fmt.Errorf("error")
return fmt.Errorf("failed")
```
### 3.5 Comments
#### 3.5.1 Package Comments
```go
// Package storage provides storage management functionality including
// ZFS pool and dataset operations, disk discovery, and storage repository management.
package storage
```
#### 3.5.2 Function Comments
```go
// CreateZFSPool creates a new ZFS pool with the specified configuration.
// It validates the pool name, checks disk availability, and creates the pool.
// Returns an error if the pool cannot be created.
func CreateZFSPool(ctx context.Context, name string, disks []string) error {
// ...
}
```
#### 3.5.3 Inline Comments
```go
// Good: explain why, not what
// Retry up to 3 times to handle transient network errors
for i := 0; i < 3; i++ {
// ...
}
// Bad: obvious comments
// Loop 3 times
for i := 0; i < 3; i++ {
// ...
}
```
### 3.6 Testing
#### 3.6.1 Test File Naming
- Test files: `*_test.go`
- Test functions: `TestFunctionName`
- Benchmark functions: `BenchmarkFunctionName`
#### 3.6.2 Test Structure
```go
func TestCreateZFSPool(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
}{
{
name: "valid pool name",
input: "tank",
wantErr: false,
},
{
name: "empty pool name",
input: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := createPool(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("createPool() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
```
### 3.7 Concurrency
#### 3.7.1 Context Usage
```go
// Good: always accept context as first parameter
func (s *Service) CreatePool(ctx context.Context, name string) error {
// Use context for cancellation and timeout
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
// ...
}
// Bad: no context
func (s *Service) CreatePool(name string) error {
// ...
}
```
#### 3.7.2 Goroutines
```go
// Good: use context for cancellation
go func() {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// ...
}()
// Bad: no cancellation mechanism
go func() {
// ...
}()
```
### 3.8 Database Operations
#### 3.8.1 Query Context
```go
// Good: use context for queries
rows, err := s.db.QueryContext(ctx, query, args...)
// Bad: no context
rows, err := s.db.Query(query, args...)
```
#### 3.8.2 Transactions
```go
// Good: use transactions for multiple operations
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
// ... operations ...
if err := tx.Commit(); err != nil {
return err
}
```
---
## 4. Frontend (TypeScript/React) Standards
### 4.1 Code Formatting
#### 4.1.1 Use Prettier
- Configure Prettier for consistent formatting
- Format on save enabled
- Maximum line length: 100 characters
#### 4.1.2 Indentation
- Use 2 spaces for indentation
- Consistent spacing in JSX
### 4.2 Naming Conventions
#### 4.2.1 Components
```typescript
// Good: PascalCase, descriptive
function StoragePage() { }
function CreatePoolModal() { }
function NetworkInterfaceCard() { }
// Bad: unclear names
function Page() { }
function Modal() { }
function Card() { }
```
#### 4.2.2 Functions
```typescript
// Good: camelCase, descriptive
function createZFSPool(name: string): Promise<ZFSPool> { }
function handleSubmit(event: React.FormEvent): void { }
function formatBytes(bytes: number): string { }
// Bad: unclear names
function create(name: string) { }
function handle(e: any) { }
function fmt(b: number) { }
```
#### 4.2.3 Variables
```typescript
// Good: camelCase, descriptive
const poolName = 'tank'
const networkInterfaces: NetworkInterface[] = []
const isActive = true
// Bad: unclear names
const n = 'tank'
const ifs: any[] = []
const a = true
```
#### 4.2.4 Constants
```typescript
// Good: UPPER_SNAKE_CASE for constants
const DEFAULT_PORT = 8080
const MAX_RETRIES = 3
const API_BASE_URL = '/api/v1'
// Bad: inconsistent casing
const defaultPort = 8080
const maxRetries = 3
```
#### 4.2.5 Types and Interfaces
```typescript
// Good: PascalCase, descriptive
interface ZFSPool {
id: string
name: string
status: string
}
type PoolStatus = 'online' | 'offline' | 'degraded'
// Bad: unclear names
interface Pool {
i: string
n: string
s: string
}
```
### 4.3 File Organization
#### 4.3.1 Component Structure
```typescript
// 1. Imports (React, third-party, local)
import { useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { zfsApi } from '@/api/storage'
// 2. Types/Interfaces
interface Props {
poolId: string
}
// 3. Component
export default function PoolDetail({ poolId }: Props) {
// 4. Hooks
const [isLoading, setIsLoading] = useState(false)
// 5. Queries
const { data: pool } = useQuery({
queryKey: ['pool', poolId],
queryFn: () => zfsApi.getPool(poolId),
})
// 6. Handlers
const handleDelete = () => {
// ...
}
// 7. Effects
useEffect(() => {
// ...
}, [poolId])
// 8. Render
return (
// JSX
)
}
```
#### 4.3.2 File Naming
- Components: `PascalCase.tsx` (e.g., `StoragePage.tsx`)
- Utilities: `camelCase.ts` (e.g., `formatBytes.ts`)
- Types: `camelCase.ts` or `types.ts`
- Hooks: `useCamelCase.ts` (e.g., `useStorage.ts`)
### 4.4 TypeScript
#### 4.4.1 Type Safety
```typescript
// Good: explicit types
function createPool(name: string): Promise<ZFSPool> {
// ...
}
// Bad: any types
function createPool(name: any): any {
// ...
}
```
#### 4.4.2 Interface Definitions
```typescript
// Good: clear interface definitions
interface ZFSPool {
id: string
name: string
status: 'online' | 'offline' | 'degraded'
totalCapacityBytes: number
usedCapacityBytes: number
}
// Bad: unclear or missing types
interface Pool {
id: any
name: any
status: any
}
```
### 4.5 React Patterns
#### 4.5.1 Hooks
```typescript
// Good: custom hooks for reusable logic
function useZFSPool(poolId: string) {
return useQuery({
queryKey: ['pool', poolId],
queryFn: () => zfsApi.getPool(poolId),
})
}
// Usage
const { data: pool } = useZFSPool(poolId)
```
#### 4.5.2 Component Composition
```typescript
// Good: small, focused components
function PoolCard({ pool }: { pool: ZFSPool }) {
return (
<div>
<PoolHeader pool={pool} />
<PoolStats pool={pool} />
<PoolActions pool={pool} />
</div>
)
}
// Bad: large, monolithic components
function PoolCard({ pool }: { pool: ZFSPool }) {
// 500+ lines of JSX
}
```
#### 4.5.3 State Management
```typescript
// Good: use React Query for server state
const { data, isLoading } = useQuery({
queryKey: ['pools'],
queryFn: zfsApi.listPools,
})
// Good: use local state for UI state
const [isModalOpen, setIsModalOpen] = useState(false)
// Good: use Zustand for global UI state
const { user, setUser } = useAuthStore()
```
### 4.6 Error Handling
#### 4.6.1 Error Boundaries
```typescript
// Good: use error boundaries
function ErrorBoundary({ children }: { children: React.ReactNode }) {
// ...
}
// Usage
<ErrorBoundary>
<App />
</ErrorBoundary>
```
#### 4.6.2 Error Handling in Queries
```typescript
// Good: handle errors in queries
const { data, error, isLoading } = useQuery({
queryKey: ['pools'],
queryFn: zfsApi.listPools,
onError: (error) => {
console.error('Failed to load pools:', error)
// Show user-friendly error message
},
})
```
### 4.7 Styling
#### 4.7.1 TailwindCSS
```typescript
// Good: use Tailwind classes
<div className="flex items-center gap-4 p-6 bg-card-dark rounded-lg border border-border-dark">
<h2 className="text-lg font-bold text-white">Storage Pools</h2>
</div>
// Bad: inline styles
<div style={{ display: 'flex', padding: '24px', backgroundColor: '#18232e' }}>
<h2 style={{ fontSize: '18px', fontWeight: 'bold', color: 'white' }}>Storage Pools</h2>
</div>
```
#### 4.7.2 Class Organization
```typescript
// Good: logical grouping
className="flex items-center gap-4 p-6 bg-card-dark rounded-lg border border-border-dark hover:bg-border-dark transition-colors"
// Bad: random order
className="p-6 flex border rounded-lg items-center gap-4 bg-card-dark border-border-dark"
```
### 4.8 Testing
#### 4.8.1 Component Testing
```typescript
// Good: test component behavior
describe('StoragePage', () => {
it('displays pools when loaded', () => {
render(<StoragePage />)
expect(screen.getByText('tank')).toBeInTheDocument()
})
it('shows loading state', () => {
render(<StoragePage />)
expect(screen.getByText('Loading...')).toBeInTheDocument()
})
})
```
---
## 5. Git Commit Standards
### 5.1 Commit Message Format
```
<type>(<scope>): <subject>
<body>
<footer>
```
### 5.2 Commit Types
- **feat**: New feature
- **fix**: Bug fix
- **docs**: Documentation changes
- **style**: Code style changes (formatting, etc.)
- **refactor**: Code refactoring
- **test**: Test additions or changes
- **chore**: Build process or auxiliary tool changes
### 5.3 Commit Examples
```
feat(storage): add ZFS pool creation endpoint
Add POST /api/v1/storage/zfs/pools endpoint with validation
and error handling.
Closes #123
fix(shares): correct dataset_id field in create share
The frontend was sending dataset_name instead of dataset_id.
Updated to use UUID from dataset selection.
docs: update API documentation for snapshot endpoints
refactor(auth): simplify JWT token validation logic
```
### 5.4 Branch Naming
- **feature/**: New features (e.g., `feature/object-storage`)
- **fix/**: Bug fixes (e.g., `fix/share-creation-error`)
- **docs/**: Documentation (e.g., `docs/api-documentation`)
- **refactor/**: Refactoring (e.g., `refactor/storage-service`)
---
## 6. Code Review Guidelines
### 6.1 Review Checklist
- [ ] Code follows naming conventions
- [ ] Code is properly formatted
- [ ] Error handling is appropriate
- [ ] Tests are included for new features
- [ ] Documentation is updated
- [ ] No security vulnerabilities
- [ ] Performance considerations addressed
- [ ] No commented-out code
- [ ] No console.log statements (use proper logging)
### 6.2 Review Comments
- Be constructive and respectful
- Explain why, not just what
- Suggest improvements, not just point out issues
- Approve when code meets standards
---
## 7. Documentation Standards
### 7.1 Code Comments
- Document complex logic
- Explain "why" not "what"
- Keep comments up-to-date
### 7.2 API Documentation
- Document all public APIs
- Include parameter descriptions
- Include return value descriptions
- Include error conditions
### 7.3 README Files
- Keep README files updated
- Include setup instructions
- Include usage examples
- Include troubleshooting tips
---
## 8. Performance Standards
### 8.1 Backend
- Database queries should be optimized
- Use indexes appropriately
- Avoid N+1 query problems
- Use connection pooling
### 8.2 Frontend
- Minimize re-renders
- Use React.memo for expensive components
- Lazy load routes
- Optimize bundle size
---
## 9. Security Standards
### 9.1 Input Validation
- Validate all user inputs
- Sanitize inputs before use
- Use parameterized queries
- Escape output
### 9.2 Authentication
- Never store passwords in plaintext
- Use secure token storage
- Implement proper session management
- Handle token expiration
### 9.3 Authorization
- Check permissions on every request
- Use principle of least privilege
- Log security events
- Handle authorization errors properly
---
## 10. Tools and Configuration
### 10.1 Backend Tools
- **gofmt**: Code formatting
- **goimports**: Import organization
- **golint**: Linting
- **go vet**: Static analysis
### 10.2 Frontend Tools
- **Prettier**: Code formatting
- **ESLint**: Linting
- **TypeScript**: Type checking
- **Vite**: Build tool
---
## 11. Exceptions
### 11.1 When to Deviate
- Performance-critical code may require optimization
- Legacy code integration may require different patterns
- Third-party library constraints
### 11.2 Documenting Exceptions
- Document why standards are not followed
- Include comments explaining deviations
- Review exceptions during code review
---
## Document History
| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0.0-alpha | 2025-01-XX | Development Team | Initial coding standards document |