Skip to main content

Coding Standards

This document outlines the coding standards and best practices for the Nounspace codebase to ensure consistency, maintainability, and code quality.

TypeScript Standards​

1. Type Safety​

  • Strict TypeScript - Use strict mode with all type checking enabled
  • Explicit Types - Define types for all function parameters and return values
  • Interface Definitions - Use interfaces for object shapes and component props
  • Generic Types - Use generics for reusable type-safe code
// Good: Explicit types
interface UserProps {
id: string;
name: string;
email: string;
isActive: boolean;
}

const User: React.FC<UserProps> = ({ id, name, email, isActive }) => {
return (
<div>
<h2>{name}</h2>
<p>{email}</p>
<span>{isActive ? 'Active' : 'Inactive'}</span>
</div>
);
};

// Good: Generic types
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}

const fetchUser = async (id: string): Promise<ApiResponse<UserProps>> => {
const response = await api.get(`/users/${id}`);
return response.data;
};

2. Type Definitions​

  • Centralized Types - Define types in dedicated files
  • Reusable Types - Create shared types for common patterns
  • Type Guards - Use type guards for runtime type checking
  • Discriminated Unions - Use discriminated unions for complex state
// types/user.ts
export interface User {
id: string;
name: string;
email: string;
role: UserRole;
}

export type UserRole = 'admin' | 'user' | 'guest';

export interface UserState {
user: User | null;
loading: boolean;
error: string | null;
}

// Type guard
export const isUser = (value: any): value is User => {
return value && typeof value.id === 'string' && typeof value.name === 'string';
};

React Standards​

1. Component Structure​

  • Functional Components - Use functional components with hooks
  • Component Props - Define clear prop interfaces
  • Default Props - Use default parameters instead of defaultProps
  • Component Composition - Prefer composition over inheritance
// Good: Functional component with clear props
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'danger';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
onClick?: () => void;
children: React.ReactNode;
}

export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'md',
disabled = false,
onClick,
children,
}) => {
return (
<button
className={`btn btn-${variant} btn-${size}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
};

2. Hooks Usage​

  • Custom Hooks - Extract reusable logic into custom hooks
  • Hook Dependencies - Always include all dependencies in useEffect
  • Hook Rules - Follow the rules of hooks consistently
  • Hook Optimization - Use useMemo and useCallback appropriately
// Good: Custom hook with proper dependencies
export const useUser = (userId: string) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
const fetchUser = async () => {
try {
setLoading(true);
const userData = await api.getUser(userId);
setUser(userData);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setLoading(false);
}
};

if (userId) {
fetchUser();
}
}, [userId]); // Include all dependencies

return { user, loading, error };
};

3. State Management​

  • Local State - Use useState for component-local state
  • Global State - Use Zustand for global state management
  • State Updates - Use functional updates for state that depends on previous state
  • State Normalization - Keep state normalized and flat
// Good: Functional state updates
const [count, setCount] = useState(0);

const increment = () => {
setCount(prevCount => prevCount + 1);
};

// Good: Normalized state
interface AppState {
users: Record<string, User>;
spaces: Record<string, Space>;
currentUserId: string | null;
}

Code Organization​

1. File Structure​

  • Atomic Design - Organize components by atomic design principles
  • Feature-based - Group related functionality together
  • Barrel Exports - Use index files for clean imports
  • Co-location - Keep related files close together
src/
├── components/
│ ├── atoms/
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ ├── Button.test.tsx
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── molecules/
│ └── organisms/
├── hooks/
├── utils/
├── types/
└── constants/

2. Import/Export Standards​

  • Named Exports - Use named exports for better tree shaking
  • Barrel Exports - Use index files for clean imports
  • Import Order - Organize imports in a consistent order
  • Absolute Imports - Use absolute imports for better refactoring
// Good: Import order
// 1. React and external libraries
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import { Button } from '@mui/material';

// 2. Internal imports (absolute paths)
import { useUser } from '@/hooks/useUser';
import { User } from '@/types/user';
import { api } from '@/utils/api';

// 3. Relative imports
import './Button.css';

3. Naming Conventions​

  • PascalCase - Use PascalCase for components and types
  • camelCase - Use camelCase for variables and functions
  • kebab-case - Use kebab-case for file names
  • Descriptive Names - Use descriptive names that explain intent
// Good: Naming conventions
// Components
export const UserProfile: React.FC<UserProfileProps> = () => {};

// Hooks
export const useUserProfile = () => {};

// Types
interface UserProfileProps {
userId: string;
showAvatar?: boolean;
}

// Files: user-profile.tsx, use-user-profile.ts

Error Handling​

1. Error Boundaries​

  • Error Boundaries - Use error boundaries to catch component errors
  • Fallback UI - Provide meaningful fallback UI for errors
  • Error Logging - Log errors for debugging and monitoring
  • User-friendly Messages - Show user-friendly error messages
// Error boundary
export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false, error: null };
}

static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { hasError: true, error };
}

componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
// Log to error reporting service
}

render() {
if (this.state.hasError) {
return (
<div className="error-boundary">
<h2>Something went wrong</h2>
<p>Please refresh the page or try again later.</p>
</div>
);
}

return this.props.children;
}
}

2. Async Error Handling​

  • Try-Catch - Use try-catch for async operations
  • Error States - Handle error states in components
  • Retry Logic - Implement retry logic for failed operations
  • Loading States - Show loading states during async operations
// Good: Async error handling
export const useAsyncOperation = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

const execute = async (operation: () => Promise<any>) => {
try {
setLoading(true);
setError(null);
const result = await operation();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setLoading(false);
}
};

return { data, loading, error, execute };
};

Performance Standards​

1. Optimization Techniques​

  • Memoization - Use React.memo, useMemo, and useCallback appropriately
  • Lazy Loading - Implement lazy loading for heavy components
  • Code Splitting - Split code into smaller chunks
  • Bundle Analysis - Regularly analyze bundle size
// Good: Memoization
export const ExpensiveComponent = React.memo<ExpensiveComponentProps>(({
data,
onUpdate,
}) => {
const processedData = useMemo(() => {
return data.map(item => processItem(item));
}, [data]);

const handleUpdate = useCallback((id: string, updates: any) => {
onUpdate(id, updates);
}, [onUpdate]);

return (
<div>
{processedData.map(item => (
<Item
key={item.id}
data={item}
onUpdate={handleUpdate}
/>
))}
</div>
);
});

2. Bundle Optimization​

  • Tree Shaking - Use named exports for better tree shaking
  • Dynamic Imports - Use dynamic imports for code splitting
  • Bundle Analysis - Use tools like webpack-bundle-analyzer
  • Dependency Management - Keep dependencies up to date
// Good: Dynamic imports
const LazyComponent = lazy(() => import('./HeavyComponent'));

export const App = () => (
<Suspense fallback={<LoadingSpinner />}>
<LazyComponent />
</Suspense>
);

Testing Standards​

1. Test Structure​

  • AAA Pattern - Arrange, Act, Assert pattern for tests
  • Test Isolation - Each test should be independent
  • Descriptive Names - Use descriptive test names
  • Test Coverage - Aim for high test coverage
// Good: Test structure
describe('UserProfile', () => {
it('should display user information when user is loaded', () => {
// Arrange
const mockUser: User = {
id: '1',
name: 'John Doe',
email: 'john@example.com',
role: 'user'
};

// Act
render(<UserProfile user={mockUser} />);

// Assert
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('john@example.com')).toBeInTheDocument();
});
});

2. Test Types​

  • Unit Tests - Test individual components and functions
  • Integration Tests - Test component interactions
  • E2E Tests - Test complete user workflows
  • Accessibility Tests - Test accessibility compliance
// Good: Integration test
describe('User Management', () => {
it('should allow user to update profile', async () => {
render(
<UserProvider>
<UserProfile />
</UserProvider>
);

const nameInput = screen.getByLabelText('Name');
const saveButton = screen.getByText('Save');

fireEvent.change(nameInput, { target: { value: 'New Name' } });
fireEvent.click(saveButton);

await waitFor(() => {
expect(screen.getByText('Profile updated')).toBeInTheDocument();
});
});
});

Documentation Standards​

1. Code Comments​

  • JSDoc Comments - Use JSDoc for function documentation
  • Inline Comments - Use inline comments for complex logic
  • TODO Comments - Use TODO comments for future improvements
  • Deprecation Comments - Mark deprecated code clearly
/**
* Fetches user data from the API
* @param userId - The unique identifier for the user
* @param options - Optional configuration for the request
* @returns Promise that resolves to user data
* @throws {Error} When the user is not found or API request fails
*/
export const fetchUser = async (
userId: string,
options?: FetchOptions
): Promise<User> => {
// TODO: Add caching mechanism
try {
const response = await api.get(`/users/${userId}`, options);
return response.data;
} catch (error) {
// Log error for debugging
console.error('Failed to fetch user:', error);
throw new Error('User not found');
}
};

2. README Files​

  • Component README - Document complex components
  • API Documentation - Document API endpoints and usage
  • Setup Instructions - Provide clear setup instructions
  • Examples - Include usage examples
# UserProfile Component

A component for displaying and editing user profile information.

## Usage

```typescript
import { UserProfile } from '@/components/UserProfile';

<UserProfile
user={user}
onUpdate={handleUpdate}
showAvatar={true}
/>

Props​

PropTypeDefaultDescription
userUser-User data to display
onUpdate(user: User) => void-Callback when user is updated
showAvatarbooleantrueWhether to show user avatar

Examples​

See the examples directory for more usage examples.


## Security Standards

### 1. Input Validation
- **Sanitization** - Sanitize all user inputs
- **Validation** - Validate data on both client and server
- **Type Safety** - Use TypeScript for compile-time type checking
- **Error Handling** - Handle validation errors gracefully

```typescript
// Good: Input validation
export const validateUserInput = (input: any): UserInput => {
const schema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().min(0).max(120),
});

return schema.parse(input);
};

2. Security Best Practices​

  • HTTPS Only - Use HTTPS for all communications
  • Input Sanitization - Sanitize all user inputs
  • XSS Prevention - Prevent cross-site scripting attacks
  • CSRF Protection - Implement CSRF protection
// Good: XSS prevention
export const sanitizeHtml = (html: string): string => {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
ALLOWED_ATTR: ['href', 'title'],
});
};

Accessibility Standards​

1. ARIA Attributes​

  • Semantic HTML - Use semantic HTML elements
  • ARIA Labels - Provide ARIA labels for screen readers
  • Keyboard Navigation - Ensure keyboard accessibility
  • Focus Management - Manage focus appropriately
// Good: Accessibility
export const AccessibleButton: React.FC<ButtonProps> = ({
children,
onClick,
disabled,
ariaLabel,
}) => {
return (
<button
onClick={onClick}
disabled={disabled}
aria-label={ariaLabel}
role="button"
tabIndex={disabled ? -1 : 0}
>
{children}
</button>
);
};

2. Testing Accessibility​

  • Screen Reader Testing - Test with screen readers
  • Keyboard Testing - Test keyboard navigation
  • Color Contrast - Ensure proper color contrast
  • Focus Indicators - Provide clear focus indicators
// Good: Accessibility testing
describe('Accessibility', () => {
it('should be accessible to screen readers', () => {
render(<UserProfile user={mockUser} />);

expect(screen.getByRole('main')).toBeInTheDocument();
expect(screen.getByLabelText('User name')).toBeInTheDocument();
});

it('should support keyboard navigation', () => {
render(<UserProfile user={mockUser} />);

const nameInput = screen.getByLabelText('User name');
nameInput.focus();
expect(nameInput).toHaveFocus();
});
});

Code Review Standards​

1. Review Checklist​

  • Functionality - Does the code work as intended?
  • Performance - Are there any performance issues?
  • Security - Are there any security vulnerabilities?
  • Accessibility - Is the code accessible?
  • Testing - Are there adequate tests?
  • Documentation - Is the code well documented?

2. Review Process​

  • Self Review - Review your own code before submitting
  • Peer Review - Get at least one peer review
  • Automated Checks - Ensure all automated checks pass
  • Documentation - Update documentation as needed

Continuous Improvement​

1. Code Quality Metrics​

  • Test Coverage - Maintain high test coverage
  • Code Complexity - Keep code complexity low
  • Performance Metrics - Monitor performance metrics
  • Security Scans - Regular security scans

2. Learning and Development​

  • Code Reviews - Learn from code reviews
  • Best Practices - Stay updated with best practices
  • Tools and Techniques - Learn new tools and techniques
  • Community - Participate in the development community