Coding Standards
Overview
Coding standards, contribution guidelines, and technical implementation details for Mind Measure developers.
Note: For deployment procedures and architecture overview, see the Development Guide.
This page covers:
- Contributing guidelines and code of conduct
- Code quality standards (TypeScript, React, CSS)
- Testing guidelines
- Integration patterns
- Security best practices
Contributing Guidelines
Code of Conduct
We are committed to making participation in the Mind Measure project harassment-free for everyone. Expected behavior includes:
- Use welcoming and inclusive language
- Be respectful of differing viewpoints and experiences
- Gracefully accept constructive criticism
- Focus on what is best for the community
- Show empathy towards other community members
- Maintain confidentiality of user data and respect privacy
Getting Started
Prerequisites
- Node.js 18+ installed
- Git configured with your name and email
- Cursor IDE (recommended)
- Basic understanding of React, TypeScript, and AWS services
Repository Setup
# Clone the repository
git clone https://github.com/ColwallKeith/mind-measure-core.git
cd mind-measure-core
# Install dependencies
npm install
# Set up environment variables
cp .env.example .env
cp apps/admin/.env.example apps/admin/.env
cp apps/mobile/.env.example apps/mobile/.env
# Start local development
npm run dev:admin # or npm run dev:mobileDevelopment Workflow
Branching Strategy
- main: Production-ready code
- develop: Integration branch for features
- feature/: Individual feature branches
- hotfix/: Critical production fixes
Feature Development Process
# Create feature branch
git checkout develop
git pull origin develop
git checkout -b feature/your-feature-name
# Make changes and commit
git add .
git commit -m "feat: add new feature description"
# Push and create PR
git push origin feature/your-feature-nameCommit Message Convention
Follow conventional commits format:
feat:- New featuresfix:- Bug fixesdocs:- Documentation changesstyle:- Code style changesrefactor:- Code refactoringtest:- Test additions or changeschore:- Maintenance tasks
Coding Standards
TypeScript Guidelines
// Use explicit types for function parameters and returns
interface UserProfile {
id: string;
email: string;
institutionId?: string;
}
const updateProfile = async (
userId: string,
updates: Partial<UserProfile>
): Promise<UserProfile> => {
// Implementation
};
// Use strict null checks
const userName = user?.profile?.name ?? 'Unknown User';React Component Standards
// Use function components with TypeScript
interface ButtonProps {
variant: 'primary' | 'secondary';
children: React.ReactNode;
onClick: () => void;
}
export const Button: React.FC<ButtonProps> = ({
variant,
children,
onClick
}) => {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{children}
</button>
);
};CSS and Styling Guidelines
/* Use Mind Measure brand colors from design system */
:root {
--color-primary: #2563eb;
--color-secondary: #7c3aed;
--color-accent: #f59e0b;
--color-success: #10b981;
--color-warning: #f59e0b;
--color-error: #ef4444;
}
/* Follow BEM naming convention for custom classes */
.dashboard-card {
/* Block */
}
.dashboard-card__header {
/* Element */
}
.dashboard-card--featured {
/* Modifier */
}AWS Lambda Functions
Lambda functions are the core of our serverless backend processing.
// Lambda function template
import { APIGatewayProxyHandler } from 'aws-lambda';
export const handler: APIGatewayProxyHandler = async (event) => {
try {
// Parse request body
const body = event.body ? JSON.parse(event.body) : {};
// Get user from Cognito authorizer
const userId = event.requestContext.authorizer?.claims?.sub;
if (!userId) {
return {
statusCode: 401,
body: JSON.stringify({ error: 'Unauthorized' })
};
}
// Process request
const result = await processRequest(body, userId);
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify(result)
};
} catch (error) {
console.error('Lambda error:', error);
return {
statusCode: 500,
body: JSON.stringify({
error: 'Internal server error',
message: error instanceof Error ? error.message : 'Unknown error'
})
};
}
};
async function processRequest(data: any, userId: string) {
// Your business logic here
return { success: true, data };
}Vercel Serverless API Functions
For the mobile app API endpoints served via Vercel:
// pages/api/database/select.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { Pool } from 'pg';
const pool = new Pool({
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT || '5432'),
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
ssl: { rejectUnauthorized: false }
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const { table, limit = 10 } = req.body;
const result = await pool.query(
`SELECT * FROM ${table} LIMIT $1`,
[limit]
);
res.status(200).json({ data: result.rows });
} catch (error) {
console.error('API error:', error);
res.status(500).json({ error: 'Database query failed' });
}
}Quality Assurance
Security Guards
# Check for hardcoded secrets
npm run guard:secrets
# Check for cross-package imports
npm run guard:cross
# Run all guards
npm run guard:allCode Quality
# Linting
npm run lint
# Type checking
npm run typecheck
# Build verification
npm run buildTesting Guidelines
// Unit test example
import { render, screen } from '@testing-library/react';
import { Button } from './Button';
describe('Button Component', () => {
it('renders with correct text', () => {
render(<Button variant="primary" onClick={() => {}}>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button variant="primary" onClick={handleClick}>Click me</Button>);
screen.getByText('Click me').click();
expect(handleClick).toHaveBeenCalledTimes(1);
});
});Code Review Checklist
- Code follows TypeScript and React best practices
- All functions have appropriate type annotations
- Components are properly tested
- Security guards pass
- Documentation is updated
- Commit messages follow convention
- No hardcoded secrets or sensitive data
- Cross-import rules are followed
Integrations
ElevenLabs Voice Integration
Setup and Configuration
// ElevenLabs client configuration
import { ElevenLabsApi } from 'elevenlabs';
const elevenlabs = new ElevenLabsApi({
apiKey: process.env.ELEVENLABS_API_KEY,
});
// Voice synthesis
const synthesizeVoice = async (text: string, voiceId: string) => {
const audio = await elevenlabs.generate({
voice: voiceId,
text: text,
model_id: "eleven_monolingual_v1"
});
return audio;
};Voice Processing Pipeline
- Text Input: User provides text for synthesis
- Voice Selection: Choose appropriate voice model
- Audio Generation: ElevenLabs API processing
- Audio Delivery: Stream audio to client
- Quality Control: Monitor synthesis quality
Integration Strategy
Third-Party Service Connections
- AWS Aurora: Primary database (PostgreSQL)
- AWS Cognito: Authentication and user management
- AWS S3: File storage and media uploads
- AWS Lambda: Serverless backend processing
- AWS Rekognition: Visual analysis
- AWS Comprehend: Text sentiment analysis
- ElevenLabs: Voice synthesis and conversational AI
- Vercel: Frontend hosting and serverless API
API Integration Patterns
// Standardized API client pattern
class ApiClient {
private baseUrl: string;
private apiKey: string;
constructor(baseUrl: string, apiKey: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) {
throw new Error(`API Error: ${response.statusText}`);
}
return response.json();
}
}Technical Implementation
Debug Implementation
Development Debugging
// Debug utilities
const DEBUG = process.env.NODE_ENV === 'development';
export const debugLog = (message: string, data?: any) => {
if (DEBUG) {
console.log(`[DEBUG] ${message}`, data);
}
};
// Error boundary for development
export class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
if (DEBUG) {
console.error('Error Boundary caught an error:', error, errorInfo);
}
}
render() {
if (this.state.hasError) {
return <div>Something went wrong. Check console for details.</div>;
}
return this.props.children;
}
}Production Monitoring
// Error tracking and monitoring
import * as Sentry from '@sentry/react';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
});
// Performance monitoring
export const trackPerformance = (name: string, fn: () => Promise<any>) => {
const transaction = Sentry.startTransaction({ name });
return fn()
.finally(() => transaction.finish());
};Migration Guide
Database Migrations
-- Migration template
-- Migration: YYYY-MM-DD-HHMMSS_migration_name.sql
-- Add new column with default value
ALTER TABLE profiles
ADD COLUMN new_field VARCHAR(255) DEFAULT 'default_value';
-- Create index for performance
CREATE INDEX idx_profiles_new_field ON profiles(new_field);
-- Update existing data if needed
UPDATE profiles
SET new_field = 'updated_value'
WHERE condition = true;Application Migrations
// Version migration helper
interface MigrationScript {
version: string;
description: string;
up: () => Promise<void>;
down: () => Promise<void>;
}
const migrations: MigrationScript[] = [
{
version: '1.1.0',
description: 'Add user preferences table',
up: async () => {
// Forward migration
},
down: async () => {
// Rollback migration
}
}
];
export const runMigrations = async (targetVersion: string) => {
// Migration execution logic
};Localization Standards
British English Spelling
- Colour not Color
- Centre not Center
- Realise not Realize
- Organisation not Organisation
- Behaviour not Behavior
Text Guidelines
// Localization helper
const text = {
en_GB: {
'welcome.message': 'Welcome to Mind Measure',
'colour.primary': 'Primary Colour',
'organisation.settings': 'Organisation Settings',
}
};
export const t = (key: string, locale: string = 'en_GB') => {
return text[locale]?.[key] || key;
};Pull Request Process
PR Requirements
- Builds successfully and passes all tests
- Code review by at least one team member
- Documentation updates for any new features
- Security guards pass all checks
- No breaking changes without major version bump
PR Template
### Summary
Brief description of what changed and why.
### Checklist
- [ ] Code builds and typechecks locally
- [ ] If backend or schema changed, docs updated under /docs
- [ ] If UX changed, screenshots added to PR
- [ ] Security guards pass
- [ ] Tests added/updated for new functionality
### Testing
How was this change tested?
### Screenshots
If UI changes, include before/after screenshots.Security Guidelines
Data Handling
- Never log sensitive data in production
- Encrypt all data in transit and at rest
- Use environment variables for secrets
- Implement proper authentication for all endpoints
- Follow principle of least privilege for database access
Code Security
// Input validation example
import { z } from 'zod';
const UserProfileSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
institutionId: z.string().uuid().optional(),
});
export const validateUserProfile = (data: unknown) => {
return UserProfileSchema.parse(data);
};Security Checklist
- No hardcoded secrets in code
- Input validation on all user inputs
- SQL injection prevention (use parameterized queries)
- XSS prevention (proper escaping)
- CSRF protection enabled
- Authentication required for protected routes
- Authorisation checks in place
- Rate limiting implemented