
code-professionalization
by Bemyself19
SKILL.md
name: Code Professionalization description: Systematically transform codebase from 6.5/10 to 9/10 professional standards through testing, type safety, refactoring, and best practices
Code Professionalization Skill
Overview
This skill guides the systematic improvement of a codebase to professional engineering standards, specifically designed for the SehatyNet project but applicable to any TypeScript/Node.js application.
Goal: Transform codebase from 6.5/10 to 9/10 professional quality Timeline: 3-6 months (can be accelerated with dedicated effort) Prerequisites: Working application with TypeScript, Node.js, React
Execution Principle
Execute phases sequentially - do not skip ahead. Each phase builds on the previous one and includes validation checkpoints. Track progress in PROGRESS.md file.
Phase 1: Critical Fixes (Week 1-2)
Goal: Fix the most glaring professional deficiencies Time: 1-2 weeks Impact: High - Makes codebase maintainable and testable
1.1 Enable TypeScript Strict Mode
Status: 🔴 Not Started
Steps:
- Backup current configuration
cp tsconfig.json tsconfig.json.backup
cp backend/tsconfig.json backend/tsconfig.json.backup
- Enable strict mode in backend (easier to start here)
// backend/tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true
}
}
- Run TypeScript compiler to see errors
cd backend && npm run build
-
Fix errors incrementally by file priority:
- Start with models (lowest dependency)
- Then services
- Then controllers
- Finally routes and index.ts
-
Common fixes needed:
- Add explicit return types to functions
- Handle null/undefined cases with optional chaining
- Replace
anytypes with proper interfaces - Add type guards for user input
-
Validate:
cd backend && npm run build
# Should compile with zero errors
- Repeat for frontend:
// tsconfig.json (frontend)
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
Checkpoint: ✅ Both frontend and backend compile with strict mode enabled
1.2 Add Testing Infrastructure
Status: 🔴 Not Started
Backend Testing Setup:
- Install testing dependencies
cd backend
npm install --save-dev jest @types/jest ts-jest supertest @types/supertest
- Create Jest configuration
cat > jest.config.js << 'EOF'
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/index.ts'
],
coverageThreshold: {
global: {
branches: 50,
functions: 50,
lines: 50,
statements: 50
}
}
};
EOF
- Update package.json scripts
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
}
- Create first test file -
backend/src/__tests__/auth.controller.test.ts:
import request from 'supertest';
import express from 'express';
import authRoutes from '../routes/auth.routes';
const app = express();
app.use(express.json());
app.use('/api/v1/auth', authRoutes);
describe('Authentication Controller', () => {
describe('POST /api/v1/auth/register', () => {
it('should reject registration without email', async () => {
const response = await request(app)
.post('/api/v1/auth/register')
.send({ password: 'Test123!@#' });
expect(response.status).toBe(400);
});
it('should reject weak passwords', async () => {
const response = await request(app).post('/api/v1/auth/register').send({
email: 'test@example.com',
password: '123',
});
expect(response.status).toBe(400);
});
});
});
- Run tests
npm test
Frontend Testing Setup:
- Install Vitest and React Testing Library
npm install --save-dev vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdom
- Create Vitest config -
vitest.config.ts:
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: ['node_modules/', 'src/test/'],
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
- Create test setup file -
src/test/setup.ts:
import { expect, afterEach } from 'vitest';
import { cleanup } from '@testing-library/react';
import * as matchers from '@testing-library/jest-dom/matchers';
expect.extend(matchers);
afterEach(() => {
cleanup();
});
- Update package.json
{
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest --coverage"
}
}
- Create first component test -
src/components/__tests__/ProtectedRoute.test.tsx:
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import ProtectedRoute from '../ProtectedRoute';
describe('ProtectedRoute', () => {
it('should redirect to login when not authenticated', () => {
const { container } = render(
<BrowserRouter>
<ProtectedRoute>
<div>Protected Content</div>
</ProtectedRoute>
</BrowserRouter>
);
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
});
});
Checkpoint: ✅ Tests run successfully with npm test in both backend and frontend
1.3 Extract Configuration from Code
Status: 🔴 Not Started
Steps:
- Create configuration directory structure
mkdir -p backend/src/config
- Extract CORS configuration -
backend/src/config/cors.config.ts:
export const getCorsOrigins = (): string[] => {
const origins = process.env.ALLOWED_ORIGINS?.split(',') || [];
// Add defaults for development
if (process.env.NODE_ENV !== 'production') {
origins.push('https://localhost:5173', 'http://localhost:5173');
}
return origins;
};
export const corsOptions = {
origin: (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => {
const allowedOrigins = getCorsOrigins();
if (!origin || allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
allowedHeaders: [
'Content-Type',
'Authorization',
'X-Requested-With',
'Cache-Control',
'Pragma',
'Expires',
'X-CSRF-Token',
'X-Session-ID',
],
};
- Extract rate limiting -
backend/src/config/rate-limit.config.ts:
import rateLimit from 'express-rate-limit';
export const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
message: {
error: 'Too many login attempts',
message: 'Too many login attempts, please try again later',
retryAfter: 15 * 60,
},
standardHeaders: true,
legacyHeaders: false,
skipSuccessfulRequests: true,
skipFailedRequests: false,
});
export const registrationLimiter = rateLimit({
windowMs: 60 * 60 * 1000,
max: 50,
message: {
error: 'Too many registration attempts',
message: 'Too many registration attempts, please try again later',
retryAfter: 60 * 60,
},
standardHeaders: true,
legacyHeaders: false,
});
export const generalLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 1000,
message: {
error: 'Too many requests',
message: 'Too many requests, please try again later',
retryAfter: 15 * 60,
},
standardHeaders: true,
legacyHeaders: false,
});
export const adminLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5000,
message: {
error: 'Too many requests',
message: 'Too many requests, please try again later',
retryAfter: 15 * 60,
},
standardHeaders: true,
legacyHeaders: false,
});
- Extract security headers -
backend/src/config/security.config.ts:
import helmet from 'helmet';
export const helmetConfig = helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: [
"'self'",
"'unsafe-inline'",
'https://accounts.google.com',
'https://apis.google.com',
'https://www.gstatic.com',
],
imgSrc: ["'self'", 'data:', 'https:', 'https://lh3.googleusercontent.com'],
connectSrc: [
"'self'",
'https://accounts.google.com',
'https://apis.google.com',
'https://www.googleapis.com',
],
frameSrc: ["'self'", 'https://accounts.google.com'],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
});
- Update .env.example with all required variables
cat > .env.example << 'EOF'
# Server
NODE_ENV=development
PORT=5000
FRONTEND_URL=https://localhost:5173
# Database
MONGO_URI=mongodb://localhost:27017/sehatynet
MONGO_SSL=false
# Security
JWT_SECRET=your_secure_jwt_secret_here
ENCRYPTION_KEY=your_encryption_key_here
# CORS
ALLOWED_ORIGINS=https://localhost:5173,http://localhost:5173
# Email (Nodemailer)
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USER=your_email@gmail.com
EMAIL_PASS=your_app_password
# Google OAuth
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
# Cloud Storage
GOOGLE_CLOUD_PROJECT_ID=your_project_id
LOCAL_UPLOADS_DIR=./uploads
EOF
- Update index.ts to use config modules
import { corsOptions } from './config/cors.config';
import { authLimiter, generalLimiter, adminLimiter } from './config/rate-limit.config';
import { helmetConfig } from './config/security.config';
// Replace hardcoded configs with imported ones
app.use(helmetConfig);
app.use(cors(corsOptions));
app.use('/api/v1/auth/login', authLimiter);
// etc.
Checkpoint: ✅ No hardcoded configuration in code, all via config files and environment variables
1.4 Fix Git Hygiene
Status: 🔴 Not Started
Steps:
- Create comprehensive .gitignore
cat > .gitignore << 'EOF'
# Dependencies
node_modules/
backend/node_modules/
# Build outputs
dist/
dist-ssr/
backend/dist/
*.local
# Environment files
.env
.env.*
!.env.example
backend/.env
backend/.env.*
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
backend/logs/
# OS files
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
Thumbs.db
# IDE
.vscode/*
!.vscode/extensions.json
.idea/
*.iml
# Test coverage
coverage/
*.lcov
.nyc_output/
# Temporary files
*.tmp
*.temp
temp/
tmp/
response.json
cookie.txt
test.txt
# Uploads (local development)
uploads/*
!uploads/.gitkeep
backend/uploads/*
!backend/uploads/.gitkeep
# Security
firebase-service-account.json
*.pem
*.key
*.crt
cert/
# Backups (should not be in repo)
*.backup
*backup*/
api-migration-backup-*/
storage-migration-backup-*/
# Python
.venv/
__pycache__/
*.py[cod]
# Generated files
forecasts/
*.csv
!inputs.csv
EOF
- Stop tracking sensitive files
# Remove from Git index but keep locally
git rm --cached .env
git rm --cached .env.local
git rm --cached .env.network
git rm --cached backend/.env
git rm -r --cached api-migration-backup-20251017-204040
git rm -r --cached storage-migration-backup-20251017-204233
# Commit the removal
git add .gitignore
git commit -m "chore: remove sensitive files and backups from version control"
- Remove sensitive data from Git history (OPTIONAL - rewrites history)
# WARNING: This rewrites Git history
# Only do this if you haven't shared the repo or can coordinate with team
# Install BFG Repo-Cleaner
wget https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar
# Remove .env files from all commits
java -jar bfg-1.14.0.jar --delete-files .env
java -jar bfg-1.14.0.jar --delete-folders api-migration-backup-20251017-204040
# Clean up
git reflog expire --expire=now --all && git gc --prune=now --aggressive
# Force push (coordinate with team!)
# git push --force
- Clean up test scripts - Move to proper location
mkdir -p scripts/tests
mv test-*.js scripts/tests/
mv check-*.js scripts/tests/
mv create-test-*.js scripts/tests/
# Update references if needed
Checkpoint: ✅ No sensitive files tracked, clean repository structure
Phase 2: Code Quality Improvements (Week 3-6)
Goal: Establish professional code organization and standards Time: 2-4 weeks Impact: Medium-High - Makes codebase maintainable and scalable
2.1 Refactor Large Files
Status: 🔴 Not Started
Target: Break down files > 300 lines
Steps:
- Refactor backend/src/index.ts (863 lines)
Create new structure:
backend/src/
├── server/
│ ├── app.ts # Express app setup
│ ├── middleware.ts # All middleware configuration
│ ├── routes.ts # Route registration
│ ├── websocket.ts # WebSocket server handling
│ └── shutdown.ts # Graceful shutdown logic
├── config/ # Already created in Phase 1
└── index.ts # Minimal entry point
New backend/src/server/app.ts:
import express from 'express';
import { setupMiddleware } from './middleware';
import { setupRoutes } from './routes';
export const createApp = (): express.Application => {
const app = express();
// Trust proxy for reverse proxy setups
app.set('trust proxy', 1);
// Setup all middleware
setupMiddleware(app);
// Setup all routes
setupRoutes(app);
return app;
};
New backend/src/server/middleware.ts:
import express from 'express';
import cors from 'cors';
import { corsOptions } from '../config/cors.config';
import { helmetConfig } from '../config/security.config';
import { authLimiter, generalLimiter, adminLimiter } from '../config/rate-limit.config';
import { requestLogger, errorLogger } from '../middleware/logger.middleware';
import { CSRFProtection } from '../middleware/csrf.middleware';
export const setupMiddleware = (app: express.Application): void => {
// Security headers
app.use(helmetConfig);
// CORS
app.use(cors(corsOptions));
// Rate limiting
app.use('/api/v1/auth/login', authLimiter);
app.use('/api/v1/auth/register', registrationLimiter);
app.use('/api/v1/', generalLimiter);
// Logging
app.use(requestLogger);
// Body parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// CSRF protection
app.use(CSRFProtection.verifyToken);
// Error logging
app.use(errorLogger);
};
Continue this pattern for routes.ts, websocket.ts, and shutdown.ts
- Refactor backend/src/controllers/auth.controller.ts (1,087 lines)
Break into:
backend/src/controllers/auth/
├── index.ts # Export all
├── register.controller.ts # Registration logic
├── login.controller.ts # Login/admin login
├── google-auth.controller.ts # Google SSO
├── password.controller.ts # Password reset/change
├── email-verification.controller.ts # Email verification
└── profile.controller.ts # Profile updates
Each file should be < 200 lines
- Validate refactoring
# Ensure everything still compiles
cd backend && npm run build
# Run existing tests
npm test
# Start the server and test manually
npm run dev
Checkpoint: ✅ No files > 300 lines, all modules properly separated
2.2 Establish Coding Standards
Status: 🔴 Not Started
Steps:
- Install Prettier and ESLint plugins
npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
cd backend && npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
- Create .prettierrc in project root
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"printWidth": 100,
"trailingComma": "es5",
"arrowParens": "always",
"endOfLine": "lf"
}
- Update .eslintrc.cjs to use Prettier
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'prettier', // Must be last
],
plugins: ['prettier'],
rules: {
'prettier/prettier': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/explicit-function-return-type': 'warn',
'no-console': ['warn', { allow: ['warn', 'error'] }],
},
};
- Create CONTRIBUTING.md
cat > CONTRIBUTING.md << 'EOF'
# Contributing Guidelines
## Code Standards
### File Organization
- **Maximum file size**: 300 lines
- **Maximum function length**: 50 lines
- **Maximum function parameters**: 4 (use objects for more)
### TypeScript
- **Strict mode**: Always enabled
- **No `any` types**: Use proper types or `unknown`
- **Explicit return types**: Required for all functions
- **Interface over type**: Prefer interfaces for objects
### Naming Conventions
- **Files**: kebab-case (e.g., `user-service.ts`)
- **Classes**: PascalCase (e.g., `UserService`)
- **Functions**: camelCase (e.g., `getUserById`)
- **Constants**: UPPER_SNAKE_CASE (e.g., `MAX_RETRY_COUNT`)
- **Interfaces**: PascalCase with optional `I` prefix (e.g., `IUser` or `User`)
### Testing
- **Coverage requirement**: 80% minimum
- **Test file naming**: `*.test.ts` or `*.spec.ts`
- **Test structure**: Arrange-Act-Assert
- **Test descriptions**: Should read like sentences
### Git Commit Messages
Follow [Conventional Commits](https://www.conventionalcommits.org/):
- `feat:` New feature
- `fix:` Bug fix
- `docs:` Documentation changes
- `style:` Code style changes (formatting)
- `refactor:` Code refactoring
- `test:` Adding or updating tests
- `chore:` Maintenance tasks
Example:
feat(auth): add two-factor authentication
- Implement TOTP-based 2FA
- Add QR code generation
- Update user model with 2FA fields
### Pull Requests
1. Create feature branch from `develop`
2. Write tests first (TDD preferred)
3. Ensure all tests pass
4. Run linter: `npm run lint`
5. Check formatting: `npm run format:check`
6. Update documentation if needed
7. Request review from at least one team member
### Code Review Checklist
- [ ] Tests cover new functionality
- [ ] No TypeScript errors or warnings
- [ ] Follows naming conventions
- [ ] No hardcoded configuration
- [ ] Error handling implemented
- [ ] Logging added for debugging
- [ ] Documentation updated
- [ ] No console.log statements (use Logger)
- [ ] Security implications considered
EOF
- Add format scripts to package.json
{
"scripts": {
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
"lint": "eslint . --ext .ts,.tsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint . --ext .ts,.tsx --fix"
}
}
- Format entire codebase
npm run format
cd backend && npm run format
Checkpoint: ✅ Code formatted consistently, linting errors fixed, guidelines documented
2.3 Database Migrations
Status: 🔴 Not Started
Steps:
- Install migration tool
cd backend
npm install --save-dev migrate-mongo
- Initialize migrations
npx migrate-mongo init
- Configure migrations - Edit
migrate-mongo-config.js:
module.exports = {
mongodb: {
url: process.env.MONGO_URI || 'mongodb://localhost:27017',
databaseName: 'sehatynet',
options: {
useNewUrlParser: true,
useUnifiedTopology: true,
},
},
migrationsDir: 'migrations',
changelogCollectionName: 'changelog',
migrationFileExtension: '.js',
};
- Create first migration - Document current schema:
npx migrate-mongo create initial-schema
Edit migrations/XXXXXX-initial-schema.js:
module.exports = {
async up(db) {
// Create indexes
await db.collection('users').createIndex({ email: 1, role: 1 }, { unique: true });
await db.collection('appointments').createIndex({ patientId: 1 });
await db.collection('appointments').createIndex({ doctorId: 1 });
// ... all existing indexes
},
async down(db) {
// Rollback logic
await db.collection('users').dropIndex('email_1_role_1');
// ... rollback all changes
},
};
- Update package.json
{
"scripts": {
"migrate:up": "migrate-mongo up",
"migrate:down": "migrate-mongo down",
"migrate:status": "migrate-mongo status",
"migrate:create": "migrate-mongo create"
}
}
Checkpoint: ✅ Migration system in place, current schema documented
Phase 3: Testing & Quality Assurance (Week 7-10)
Goal: Achieve 80%+ test coverage and establish quality gates Time: 3-4 weeks Impact: High - Ensures reliability and enables safe refactoring
3.1 Unit Tests - Backend
Status: 🔴 Not Started
Goal: 80% coverage of backend
Priority Test Areas (in order):
-
Authentication -
src/controllers/auth/__tests__/- Registration validation
- Login success/failure
- JWT token generation
- Password reset flow
- Email verification
-
Authorization -
src/middleware/__tests__/- Role-based access control
- Token validation
- CSRF protection
-
Models -
src/models/__tests__/- Schema validation
- Field encryption/decryption
- Hooks and middleware
-
Services -
src/services/__tests__/- Business logic
- Data transformations
- External API calls (mocked)
-
Controllers - Integration tests
- API endpoint responses
- Error handling
- Data validation
Example Test Template:
// src/services/__tests__/user.service.test.ts
import { UserService } from '../user.service';
import User from '../../models/user.model';
jest.mock('../../models/user.model');
describe('UserService', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('getUserById', () => {
it('should return user when found', async () => {
const mockUser = { _id: '123', email: 'test@example.com' };
(User.findById as jest.Mock).mockResolvedValue(mockUser);
const result = await UserService.getUserById('123');
expect(result).toEqual(mockUser);
expect(User.findById).toHaveBeenCalledWith('123');
});
it('should throw error when user not found', async () => {
(User.findById as jest.Mock).mockResolvedValue(null);
await expect(UserService.getUserById('123')).rejects.toThrow('User not found');
});
});
});
Run coverage report:
cd backend
npm run test:coverage
Checkpoint: ✅ 80%+ test coverage on backend, all critical paths tested
3.2 Unit Tests - Frontend
Status: 🔴 Not Started
Goal: 70%+ coverage of frontend (slightly lower due to UI complexity)
Priority Test Areas:
-
Hooks -
src/hooks/__tests__/- Custom hooks logic
- State management
-
Components -
src/components/__tests__/- Rendering logic
- User interactions
- Props handling
-
Utilities -
src/utils/__tests__/orsrc/lib/__tests__/- Helper functions
- Formatters
- Validators
Example Component Test:
// src/components/__tests__/LoginForm.test.tsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import LoginForm from '../LoginForm';
describe('LoginForm', () => {
it('should display validation errors for empty fields', async () => {
render(<LoginForm />);
const submitButton = screen.getByRole('button', { name: /login/i });
fireEvent.click(submitButton);
await waitFor(() => {
expect(screen.getByText(/email is required/i)).toBeInTheDocument();
expect(screen.getByText(/password is required/i)).toBeInTheDocument();
});
});
it('should call onSubmit with form data when valid', async () => {
const mockOnSubmit = vi.fn();
render(<LoginForm onSubmit={mockOnSubmit} />);
fireEvent.change(screen.getByLabelText(/email/i), {
target: { value: 'test@example.com' }
});
fireEvent.change(screen.getByLabelText(/password/i), {
target: { value: 'SecurePass123!' }
});
fireEvent.click(screen.getByRole('button', { name: /login/i }));
await waitFor(() => {
expect(mockOnSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'SecurePass123!'
});
});
});
});
Checkpoint: ✅ 70%+ test coverage on frontend, critical components tested
3.3 End-to-End Tests
Status: 🔴 Not Started
Goal: Cover critical user journeys
Install Playwright:
npm install --save-dev @playwright/test
npx playwright install
Create E2E test structure:
tests/
├── e2e/
│ ├── auth/
│ │ ├── login.spec.ts
│ │ ├── register.spec.ts
│ │ └── password-reset.spec.ts
│ ├── appointments/
│ │ ├── book-appointment.spec.ts
│ │ └── cancel-appointment.spec.ts
│ └── prescriptions/
│ └── create-prescription.spec.ts
└── playwright.config.ts
Example E2E Test:
// tests/e2e/auth/login.spec.ts
import { test, expect } from '@playwright/test';
test.describe('User Login', () => {
test('should successfully login with valid credentials', async ({ page }) => {
await page.goto('https://localhost:5173');
await page.click('text=Login');
await page.fill('input[name="email"]', 'doctor@example.com');
await page.fill('input[name="password"]', 'SecurePass123!');
await page.click('button[type="submit"]');
await expect(page).toHaveURL(/.*dashboard/);
await expect(page.locator('text=Welcome')).toBeVisible();
});
test('should display error with invalid credentials', async ({ page }) => {
await page.goto('https://localhost:5173');
await page.click('text=Login');
await page.fill('input[name="email"]', 'wrong@example.com');
await page.fill('input[name="password"]', 'wrongpass');
await page.click('button[type="submit"]');
await expect(page.locator('text=Invalid credentials')).toBeVisible();
});
});
Critical Flows to Test:
- Patient registration and login
- Doctor login and dashboard access
- Appointment booking (full flow)
- Prescription creation
- Medical record viewing
- Live consultation joining
Checkpoint: ✅ All critical user journeys have E2E tests
3.4 CI/CD Quality Gates
Status: 🔴 Not Started
Update .github/workflows/ci.yml:
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
test-backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: |
cd backend
npm ci
- name: Run TypeScript compiler
run: |
cd backend
npm run build
- name: Run tests
run: |
cd backend
npm test
- name: Check test coverage
run: |
cd backend
npm run test:coverage -- --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80,"statements":80}}'
- name: Lint
run: |
cd backend
npm run lint
test-frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run TypeScript compiler
run: npm run build
- name: Run tests
run: npm test
- name: Check test coverage
run: npm run test:coverage -- --coverage.threshold.lines=70
- name: Lint
run: npm run lint
security-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run npm audit
run: |
npm audit --audit-level=moderate
cd backend && npm audit --audit-level=moderate
- name: Check for vulnerabilities
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
e2e-tests:
runs-on: ubuntu-latest
needs: [test-backend, test-frontend]
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Install Playwright
run: npx playwright install --with-deps
- name: Run E2E tests
run: npm run test:e2e
- name: Upload test results
if: always()
uses: actions/upload-artifact@v3
with:
name: playwright-report
path: playwright-report/
Checkpoint: ✅ CI/CD pipeline blocks merges if tests fail or coverage drops
Phase 4: API & Documentation (Week 11-12)
Goal: Add comprehensive API documentation and developer guides Time: 1-2 weeks Impact: Medium - Improves maintainability and onboarding
4.1 API Documentation with Swagger
Status: 🔴 Not Started
Install dependencies:
cd backend
npm install --save swagger-jsdoc swagger-ui-express
npm install --save-dev @types/swagger-jsdoc @types/swagger-ui-express
Create Swagger configuration - backend/src/config/swagger.config.ts:
import swaggerJsdoc from 'swagger-jsdoc';
const options: swaggerJsdoc.Options = {
definition: {
openapi: '3.0.0',
info: {
title: 'SehatyNet API',
version: '1.0.0',
description: 'Telehealth platform API documentation',
contact: {
name: 'SehatyNet Team',
email: 'support@sehatynet.com',
},
},
servers: [
{
url: 'http://localhost:5000/api/v1',
description: 'Development server',
},
{
url: 'https://sehatynet.com/api/v1',
description: 'Production server',
},
],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
},
},
security: [
{
bearerAuth: [],
},
],
},
apis: ['./src/routes/*.ts', './src/controllers/**/*.ts'],
};
export const swaggerSpec = swaggerJsdoc(options);
Add to server - in backend/src/server/app.ts:
import swaggerUi from 'swagger-ui-express';
import { swaggerSpec } from '../config/swagger.config';
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
Document endpoints - Example in backend/src/routes/auth.routes.ts:
/**
* @swagger
* /auth/register:
* post:
* summary: Register a new user
* tags: [Authentication]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - email
* - password
* - firstName
* - lastName
* - role
* properties:
* email:
* type: string
* format: email
* password:
* type: string
* format: password
* minLength: 8
* firstName:
* type: string
* lastName:
* type: string
* role:
* type: string
* enum: [patient, doctor, pharmacy, lab, radiologist]
* responses:
* 201:
* description: User created successfully
* 400:
* description: Invalid input
* 409:
* description: Email already exists
*/
router.post('/register', authController.register);
Checkpoint: ✅ All API endpoints documented, accessible at /api-docs
4.2 Consolidate Documentation
Status: 🔴 Not Started
Organize documentation:
docs/
├── README.md # Overview
├── getting-started/
│ ├── installation.md
│ ├── local-development.md
│ └── deployment.md
├── architecture/
│ ├── overview.md
│ ├── database-schema.md
│ ├── authentication.md
│ └── file-storage.md
├── api/
│ └── README.md # Link to Swagger docs
├── user-guides/
│ ├── patient.md
│ ├── doctor.md
│ ├── pharmacy.md
│ └── admin.md
├── compliance/
│ ├── hipaa.md
│ ├── gdpr.md
│ └── security-audit.md
└── contributing/
├── code-standards.md # Link to CONTRIBUTING.md
├── testing-guide.md
└── release-process.md
Migrate existing 138 MD files into this structure
Update root README.md with professional structure:
# SehatyNet - Telehealth Platform
[]()
[]()
[]()
## Quick Start
```bash
npm install
npm run dev
```
Documentation
Features
- 🏥 Multi-role support (Patient, Doctor, Pharmacy, Lab, etc.)
- 📹 Live video consultations
- 📋 Digital prescriptions
- 🔒 HIPAA & GDPR compliant
- 🌍 Multi-language support
- 📱 Mobile responsive
Technology Stack
- Frontend: React 19, TypeScript, Tailwind CSS
- Backend: Node.js, Express, TypeScript
- Database: MongoDB with Mongoose
- Real-time: WebSockets, WebRTC
- Infrastructure: Docker, Cloud Run, Nginx
License
MIT © SehatyNet Team
**Checkpoint**: ✅ Documentation consolidated, discoverable, and up-to-date
---
## Phase 5: Performance & Polish (Week 13-16)
**Goal**: Optimize performance and add production-grade features
**Time**: 3-4 weeks
**Impact**: Medium-High - Production readiness
### 5.1 Add Caching Layer
**Status**: 🔴 Not Started
**Install Redis**:
```bash
cd backend
npm install redis ioredis
npm install --save-dev @types/redis
Create cache service - backend/src/services/cache.service.ts:
import Redis from 'ioredis';
import { Logger } from '../utils/logger.utils';
class CacheService {
private client: Redis;
constructor() {
this.client = new Redis({
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT || '6379'),
password: process.env.REDIS_PASSWORD,
retryStrategy: (times) => {
const delay = Math.min(times * 50, 2000);
return delay;
},
});
this.client.on('error', (err) => {
Logger.error('Redis connection error', err);
});
this.client.on('connect', () => {
Logger.info('Redis connected successfully');
});
}
async get<T>(key: string): Promise<T | null> {
try {
const value = await this.client.get(key);
return value ? JSON.parse(value) : null;
} catch (error) {
Logger.error(`Cache get error for key ${key}`, error as Error);
return null;
}
}
async set(key: string, value: any, ttlSeconds: number = 3600): Promise<void> {
try {
await this.client.setex(key, ttlSeconds, JSON.stringify(value));
} catch (error) {
Logger.error(`Cache set error for key ${key}`, error as Error);
}
}
async del(key: string): Promise<void> {
try {
await this.client.del(key);
} catch (error) {
Logger.error(`Cache delete error for key ${key}`, error as Error);
}
}
async flush(): Promise<void> {
try {
await this.client.flushall();
} catch (error) {
Logger.error('Cache flush error', error as Error);
}
}
}
export default new CacheService();
Use in controllers:
import CacheService from '../services/cache.service';
// Example: Cache user profile
export const getProfile = async (req: Request, res: Response): Promise<void> => {
const userId = req.user!._id;
const cacheKey = `user:${userId}`;
// Try cache first
const cachedUser = await CacheService.get(cacheKey);
if (cachedUser) {
return res.json(cachedUser);
}
// Fetch from database
const user = await User.findById(userId);
// Cache for 1 hour
await CacheService.set(cacheKey, user, 3600);
res.json(user);
};
Add Redis to docker-compose.yml:
services:
redis:
image: redis:7-alpine
ports:
- '6379:6379'
volumes:
- redis-data:/data
volumes:
redis-data:
Checkpoint: ✅ Redis caching implemented for frequently accessed data
5.2 Database Query Optimization
Status: 🔴 Not Started
Steps:
- Add query profiling
// Enable profiling in development
if (process.env.NODE_ENV === 'development') {
mongoose.set('debug', true);
}
- Add indexes for common queries
// In models
userSchema.index({ email: 1 });
userSchema.index({ role: 1, active: 1 });
appointmentSchema.index({ patientId: 1, date: -1 });
appointmentSchema.index({ doctorId: 1, status: 1 });
appointmentSchema.index({ date: 1, status: 1 });
medicalRecordSchema.index({ patientId: 1, createdAt: -1 });
- Implement pagination middleware:
// backend/src/middleware/pagination.middleware.ts
export const paginate = (defaultLimit: number = 20, maxLimit: number = 100) => {
return (req: Request, res: Response, next: NextFunction) => {
const page = Math.max(1, parseInt(req.query.page as string) || 1);
const limit = Math.min(
maxLimit,
Math.max(1, parseInt(req.query.limit as string) || defaultLimit)
);
const skip = (page - 1) * limit;
req.pagination = { page, limit, skip };
next();
};
};
- Use lean() for read-only queries:
// Instead of:
const users = await User.find({ role: 'patient' });
// Use:
const users = await User.find({ role: 'patient' }).lean();
- Optimize population:
// Instead of eager loading everything:
const appointments = await Appointment.find().populate('patientId').populate('doctorId');
// Select only needed fields:
const appointments = await Appointment.find()
.populate('patientId', 'firstName lastName email')
.populate('doctorId', 'firstName lastName specialty');
Checkpoint: ✅ Common queries optimized, pagination implemented, indexes added
5.3 Error Tracking & Monitoring
Status: 🔴 Not Started
Install Sentry:
npm install @sentry/node @sentry/react
cd backend && npm install @sentry/node
Configure Sentry - Backend:
// backend/src/config/sentry.config.ts
import * as Sentry from '@sentry/node';
export const initSentry = () => {
if (process.env.SENTRY_DSN) {
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV || 'development',
tracesSampleRate: 0.1,
beforeSend(event, hint) {
// Don't send development errors
if (process.env.NODE_ENV === 'development') {
return null;
}
return event;
},
});
}
};
Add to server startup:
import { initSentry } from './config/sentry.config';
initSentry();
Configure Sentry - Frontend:
// src/config/sentry.config.ts
import * as Sentry from '@sentry/react';
export const initSentry = () => {
if (import.meta.env.VITE_SENTRY_DSN) {
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.MODE,
integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()],
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
}
};
Checkpoint: ✅ Error tracking configured, production errors monitored
Validation & Metrics
After completing all phases, validate your progress:
Code Quality Metrics
Run these commands to verify improvements:
# Backend
cd backend
# Type safety
npm run build # Should complete without errors
# Test coverage
npm run test:coverage # Should show 80%+ coverage
# Code quality
npm run lint # Should have zero errors
# Frontend
cd ..
# Type safety
npm run build # Should complete without errors
# Test coverage
npm run test:coverage # Should show 70%+ coverage
# Code quality
npm run lint # Should have zero errors
Professional Checklist
- Testing: 80%+ coverage, all critical paths tested
- Type Safety: Strict TypeScript, no
anytypes - Code Organization: No files > 300 lines
- Configuration: No hardcoded values
- Documentation: API docs, architecture docs, user guides
- Security: Dependency scanning, secrets management
- Performance: Caching, query optimization, pagination
- CI/CD: Quality gates, automated testing
- Monitoring: Error tracking, logging, metrics
- Git: Clean history, no sensitive files
Final Score Estimation
Track your score improvement:
| Category | Before | Target | Current |
|---|---|---|---|
| Testing | 0/10 | 9/10 | ___/10 |
| Type Safety | 3/10 | 9/10 | ___/10 |
| Code Organization | 5/10 | 9/10 | ___/10 |
| Documentation | 6/10 | 9/10 | ___/10 |
| Security | 7/10 | 9/10 | ___/10 |
| Performance | 5/10 | 8/10 | ___/10 |
| Overall | 6.5/10 | 9/10 | ___/10 |
Skill Usage Instructions
Starting a Phase
- Read the entire phase description
- Update PROGRESS.md with phase start
- Create a feature branch for the phase
- Work through steps sequentially
- Validate at each checkpoint
- Create PR when phase complete
- Update PROGRESS.md with completion
When to Ask for Help
- TypeScript errors seem impossible to fix
- Test coverage stuck below target
- Performance optimizations not working
- Breaking changes to API
- Migration issues
Tracking Progress
Create PROGRESS.md in project root:
# Code Professionalization Progress
## Phase 1: Critical Fixes
- [x] 1.1 Enable TypeScript Strict Mode
- [x] 1.2 Add Testing Infrastructure
- [ ] 1.3 Extract Configuration
- [ ] 1.4 Fix Git Hygiene
**Status**: In Progress
**Started**: 2026-01-19
**Estimated Completion**: 2026-02-02
## Phase 2: Code Quality
- [ ] 2.1 Refactor Large Files
- [ ] 2.2 Establish Coding Standards
- [ ] 2.3 Database Migrations
**Status**: Not Started
<!-- etc. -->
Success Criteria
Phase 1 Complete ✅
- TypeScript compiles in strict mode
- At least 20 tests passing
- No hardcoded configuration
- Clean Git history
Phase 2 Complete ✅
- No files > 300 lines
- Code formatted consistently
- Migration system in place
- CONTRIBUTING.md created
Phase 3 Complete ✅
- 80%+ backend coverage
- 70%+ frontend coverage
- E2E tests for critical flows
- CI/CD blocking on test failures
Phase 4 Complete ✅
- All endpoints documented
- Documentation consolidated
- Developer onboarding guide
Phase 5 Complete ✅
- Caching implemented
- Queries optimized
- Error tracking enabled
- Performance benchmarked
Overall Success 🎯
- Code score improved from 6.5/10 to 9/10
- Professional code review would pass
- Safe to onboard new developers
- Production-ready for enterprise
Notes
- Don't skip phases - Each builds on the previous
- Validate at checkpoints - Don't move forward if checkpoints fail
- Track progress - Update PROGRESS.md regularly
- Get code reviews - Have someone review each phase
- Celebrate wins - Each phase complete is a major achievement
This is a marathon, not a sprint. Professional code quality is built incrementally.
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です