16 KiB
16 KiB
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
gofmtbefore committing - Use
goimportsfor 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
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
// 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
// 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
// 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
// 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
// Good: PascalCase, descriptive
function StoragePage() { }
function CreatePoolModal() { }
function NetworkInterfaceCard() { }
// Bad: unclear names
function Page() { }
function Modal() { }
function Card() { }
4.2.2 Functions
// 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
// 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
// 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
// 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
// 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.tsortypes.ts - Hooks:
useCamelCase.ts(e.g.,useStorage.ts)
4.4 TypeScript
4.4.1 Type Safety
// Good: explicit types
function createPool(name: string): Promise<ZFSPool> {
// ...
}
// Bad: any types
function createPool(name: any): any {
// ...
}
4.4.2 Interface Definitions
// 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
// 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
// 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
// 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
// Good: use error boundaries
function ErrorBoundary({ children }: { children: React.ReactNode }) {
// ...
}
// Usage
<ErrorBoundary>
<App />
</ErrorBoundary>
4.6.2 Error Handling in Queries
// 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
// 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
// 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
// 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 |