You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Interfaces/Types: PascalCase with 'I' prefix for interfaces (e.g., IUserData, AuthToken)
Backend Patterns
Common Libraries & Standards
Mock Creation: Always use @golevelup/ts-jest createMock
Date/Time: Always use moment with UTC (moment.utc())
Validation: Always use joi for schema validation
Array/Object Operations: Always use lodash (_) for safety - never use native array methods
Type Declarations: ALWAYS explicitly declare types for ALL variables (const, let, function parameters, return types)
CRITICAL: Type Safety Rules
// ❌ NEVER use 'as' or 'any' type assertionsconstmock={method: jest.fn()}asany;// FORBIDDENconstdata={}asUser;// FORBIDDEN// ❌ NEVER omit type declarationsconstname="John";// FORBIDDENletcount=0;// FORBIDDENfunctiongetUser(id: string){}// FORBIDDEN - missing return type// ✅ ALWAYS use proper typing and explicit declarationsimport{createMock}from'@golevelup/ts-jest';constmock=createMock<Service>({method: jest.fn()});constdata=createMock<User>({id: '123',name: 'John'});constname: string="John";// REQUIREDletcount: number=0;// REQUIREDfunctiongetUser(id: string): User{}// REQUIRED - return type declared
Date/Time Handling
import*asmomentfrom'moment';// ✅ ALWAYS use moment.utc() with explicit typesconstnow: moment.Moment=moment.utc();consttomorrow: moment.Moment=moment.utc().add(1,'day');constformatted: string=moment.utc().format('YYYY-MM-DD HH:mm:ss');// ❌ NEVER use native Dateconstnow=newDate();// FORBIDDEN
Validation with Joi
import*asJoifrom'joi';constschema: Joi.ObjectSchema=Joi.object({email: Joi.string().email().required(),age: Joi.number().integer().min(18),createdAt: Joi.date().iso(),});const{ error, value }=schema.validate(data);if(error){thrownewError(error.message);}
Array/Object Operations with Lodash
import*as_from'lodash';// ✅ ALWAYS use lodash with explicit return typesconstfiltered: User[]=_.filter(users,{status: 'active'});constmapped: string[]=_.map(users,'name');constgrouped: _.Dictionary<User[]>=_.groupBy(users,'department');constsorted: User[]=_.orderBy(users,['createdAt'],['desc']);constfound: User|undefined=_.find(users,{id: '123'});constunique: User[]=_.uniqBy(users,'email');// ❌ NEVER use native array methodsconstfiltered=users.filter(u=>u.status==='active');// FORBIDDENconstmapped=users.map(u=>u.name);// FORBIDDEN
Hapi Framework
Use route handlers with request/reply pattern
Implement validation using Joi schemas
Structure routes in separate modules
Use plugins for cross-cutting concerns
Handle errors with Boom
NestJS
Use decorators for controllers, services, modules
Implement dependency injection via constructor
Follow module-based architecture
Use DTOs (class-based) for request/response validation with Joi and Swagger decorators
Implement guards for authentication/authorization
Use interceptors for cross-cutting concerns
NestJS DTO Pattern:
import{ApiProperty}from'@nestjs/swagger';import*asJoifrom'joi';// ✅ CORRECT - DTOs as classes with Joi validation and Swagger decoratorsexportclassCreateProductDto{
@ApiProperty({example: 'Laptop Pro 15',description: 'Product name',required: true})name: string;
@ApiProperty({example: 1299.99,description: 'Product price in USD',minimum: 0,required: true})price: number;
@ApiProperty({example: 'electronics',description: 'Product category',enum: ['electronics','clothing','food'],required: true})category: string;
@ApiProperty({example: 'High-performance laptop',description: 'Product description',required: false})description?: string;// Joi schema for validationstaticreadonlyschema: Joi.ObjectSchema=Joi.object({name: Joi.string().min(3).max(100).required(),price: Joi.number().positive().precision(2).required(),category: Joi.string().valid('electronics','clothing','food').required(),description: Joi.string().max(500).optional(),});}exportclassUpdateProductDto{
@ApiProperty({example: 'Laptop Pro 16',required: false})name?: string;
@ApiProperty({example: 1399.99,minimum: 0,required: false})price?: number;
@ApiProperty({enum: ['electronics','clothing','food'],required: false})category?: string;staticreadonlyschema: Joi.ObjectSchema=Joi.object({name: Joi.string().min(3).max(100).optional(),price: Joi.number().positive().precision(2).optional(),category: Joi.string().valid('electronics','clothing','food').optional(),});}// Controller usage
@Controller('products')
@ApiTags('products')exportclassProductController{constructor(privatereadonlyproductService: ProductService){}
@Post()
@ApiOperation({summary: 'Create a new product'})
@ApiResponse({status: 201,description: 'Product created successfully'})
@ApiResponse({status: 400,description: 'Invalid input'})asynccreate(@Body()createProductDto: CreateProductDto): Promise<Product>{// Validate with Joiconst{ error, value }=CreateProductDto.schema.validate(createProductDto);if(error){thrownewBadRequestException(error.message);}constproduct: Product=awaitthis.productService.create(value);returnproduct;}
@Patch(':id')
@ApiOperation({summary: 'Update a product'})asyncupdate(
@Param('id')id: string,
@Body()updateProductDto: UpdateProductDto,): Promise<Product>{const{ error, value }=UpdateProductDto.schema.validate(updateProductDto);if(error){thrownewBadRequestException(error.message);}constproduct: Product=awaitthis.productService.update(id,value);returnproduct;}}
Nx Workspace
Each app/library should have clear boundaries
Use @nx/ scoped imports for workspace libraries
Follow the project.json configuration pattern
Use tags for enforcing module boundaries
Keep shared code in libs, apps should be thin
Frontend Patterns
React
Use functional components with hooks
Prefer TypeScript interfaces for props
Use React Query or SWR for data fetching
Implement error boundaries
Keep components small and composable
Use proper key props in lists
AngularJS (Legacy)
Maintain existing patterns when modifying
Don't mix Angular and AngularJS code
Plan migration path to modern framework
Document any legacy-specific workarounds
Kubernetes & Deployment
Environment Configuration
Use separate YAML manifests per environment
Store secrets in Kubernetes secrets, never in code
Use ConfigMaps for non-sensitive configuration
Follow 12-factor app principles
Skaffold
Local development uses skaffold dev for hot reload
Each service should have proper health checks
Use resource limits and requests
Implement graceful shutdown handlers
Best Practices
Always specify resource limits in k8s manifests
Use readiness and liveness probes
Implement proper logging (structured JSON logs)
Use namespaces for environment separation
Tag images properly (never use :latest in prod)
Testing Requirements
TDD Test Structure
Follow this pattern for all new code:
import{createMock}from'@golevelup/ts-jest';import*asmomentfrom'moment';// Example: user-service.spec.tsdescribe('UserService',()=>{letuserService: UserService;letmockRepository: jest.Mocked<UserRepository>;letmockEmailService: jest.Mocked<EmailService>;beforeEach(()=>{// CORRECT: Use createMock<T> - NO 'as any' allowedmockRepository=createMock<UserRepository>({save: jest.fn(),findById: jest.fn(),});mockEmailService=createMock<EmailService>({sendWelcomeEmail: jest.fn(),});userService=newUserService(mockRepository,mockEmailService);});// CRITICAL: Always use '#' prefix for method/function names in describe blocksdescribe('#createUser',()=>{it('should create a user with valid data',async()=>{// Arrangeconstnow: moment.Moment=moment.utc();// Always use UTCconstuserData: CreateUserDto={email: 'test@example.com',name: 'Test User',createdAt: now.toDate(),};constsavedUser: User=createMock<User>({id: '123',email: userData.email,name: userData.name,createdAt: now.toDate(),});mockRepository.save.mockResolvedValue(savedUser);// Actconstresult: User=awaituserService.createUser(userData);// Assert - Data equalityexpect(result).toEqual(savedUser);expect(result.email).toBe('test@example.com');expect(result.name).toBe('Test User');expect(moment.utc(result.createdAt).isSame(now,'second')).toBe(true);// Assert - Function callsexpect(mockRepository.save).toHaveBeenCalled();expect(mockRepository.save).toHaveBeenCalledTimes(1);expect(mockRepository.save).toHaveBeenCalledWith(userData);expect(mockEmailService.sendWelcomeEmail).toHaveBeenCalled();expect(mockEmailService.sendWelcomeEmail).toHaveBeenCalledWith(savedUser.email);});it('should throw error when email is invalid',async()=>{// ArrangeconstuserData: CreateUserDto={email: 'invalid',name: 'Test User',createdAt: moment.utc().toDate(),};// Act & Assertawaitexpect(userService.createUser(userData)).rejects.toThrow('Invalid email');// Assert - Functions NOT called due to early throwexpect(mockRepository.save).not.toHaveBeenCalled();expect(mockEmailService.sendWelcomeEmail).not.toHaveBeenCalled();});it('should handle repository errors gracefully',async()=>{// ArrangeconstuserData: CreateUserDto={email: 'test@example.com',name: 'Test User',createdAt: moment.utc().toDate(),};mockRepository.save.mockRejectedValue(newError('Database error'));// Act & Assertawaitexpect(userService.createUser(userData)).rejects.toThrow('Database error');// Assert - Save was called but email was not sent due to errorexpect(mockRepository.save).toHaveBeenCalledTimes(1);expect(mockEmailService.sendWelcomeEmail).not.toHaveBeenCalled();});});describe('#updateUserStatus',()=>{it('should call different methods based on status',async()=>{// ArrangeconstuserId: string='123';constexistingUser: User=createMock<User>({id: userId,status: 'pending',updatedAt: moment.utc().toDate(),});mockRepository.findById.mockResolvedValue(existingUser);// ActawaituserService.updateUserStatus(userId,'active');// Assert - Verify call order and argumentsexpect(mockRepository.findById).toHaveBeenNthCalledWith(1,userId);expect(mockRepository.save).toHaveBeenNthCalledWith(1,expect.objectContaining({id: userId,status: 'active'}));expect(mockEmailService.sendWelcomeEmail).toHaveBeenCalledTimes(1);});it('should not send email when status is not active',async()=>{// ArrangeconstuserId: string='123';constexistingUser: User=createMock<User>({id: userId,status: 'pending',});mockRepository.findById.mockResolvedValue(existingUser);// ActawaituserService.updateUserStatus(userId,'suspended');// Assert - If/else logic verificationexpect(mockRepository.save).toHaveBeenCalled();expect(mockEmailService.sendWelcomeEmail).not.toHaveBeenCalled();// Not called for suspended status});});describe('#filterActiveUsers',()=>{it('should filter active users using lodash',()=>{// Arrangeconstusers: User[]=[createMock<User>({id: '1',status: 'active'}),createMock<User>({id: '2',status: 'inactive'}),createMock<User>({id: '3',status: 'active'}),];// Actconstresult: User[]=userService.filterActiveUsers(users);// Assertexpect(result).toHaveLength(2);expect(result[0].id).toBe('1');expect(result[1].id).toBe('3');});});});
Test Structure & Naming Conventions
describe() Block Naming:
// ✅ CORRECT - Class/Service name (no prefix)describe('UserService',()=>{// ✅ CORRECT - Method/Function names MUST have '#' prefixdescribe('#createUser',()=>{});describe('#updateUser',()=>{});describe('#deleteUser',()=>{});describe('#findById',()=>{});});// ✅ CORRECT - For functionsdescribe('utils',()=>{describe('#calculateTotal',()=>{});describe('#formatDate',()=>{});});// ❌ WRONG - Missing '#' prefix for methods/functionsdescribe('createUser',()=>{});// FORBIDDENdescribe('calculateTotal',()=>{});// FORBIDDEN
it() Block Naming:
// ✅ CORRECT - Descriptive behaviorit('should create a user with valid data',()=>{});it('should throw error when email is invalid',()=>{});it('should not call service when validation fails',()=>{});it('should return empty array when no users found',()=>{});
Unit Test Best Practices
CRITICAL RULES - NEVER VIOLATE:
NO 'as' or 'any' Types in Tests
// ❌ WRONG - Never do thisconstmock={save: jest.fn()}asany;constuser={}asUser;// ✅ CORRECT - Always use createMockconstmock=createMock<UserRepository>({save: jest.fn(),});constuser=createMock<User>({id: '123',email: 'test@example.com',});
Mock Creation
import{createMock}from'@golevelup/ts-jest';// Always use createMock<T> for type safetyconstmockService=createMock<MyService>({method: jest.fn(),});constmockData=createMock<DataType>({field: 'value',});
Date/Time Handling
import*asmomentfrom'moment';// ✅ ALWAYS use moment with UTCconstnow=moment.utc();consttomorrow=moment.utc().add(1,'day');constlastWeek=moment.utc().subtract(7,'days');// ❌ NEVER use native Date directly// const now = new Date(); // WRONG// Format for comparisonsconstdateStr=moment.utc().format('YYYY-MM-DD');// Compare datesconstisSame=moment.utc(date1).isSame(date2,'day');constisAfter=moment.utc(date1).isAfter(date2);
Validation with Joi
import*asJoifrom'joi';constuserSchema=Joi.object({email: Joi.string().email().required(),name: Joi.string().min(3).max(50).required(),age: Joi.number().integer().min(18).max(120),createdAt: Joi.date().iso(),});// In testsit('should validate user data',()=>{constuserData=createMock<CreateUserDto>({email: 'test@example.com',name: 'John',});const{ error, value }=userSchema.validate(userData);expect(error).toBeUndefined();expect(value).toMatchObject(userData);});
Array/Object Operations - Always Use Lodash
import*as_from'lodash';// ✅ CORRECT - Use lodash for safetyconstactiveUsers=_.filter(users,{status: 'active'});constuserNames=_.map(users,'name');constgrouped=_.groupBy(users,'department');constsorted=_.orderBy(users,['createdAt'],['desc']);constunique=_.uniqBy(users,'email');constfound=_.find(users,{id: '123'});// ❌ WRONG - Don't use native array methods// const activeUsers = users.filter(u => u.status === 'active'); // NO// const userNames = users.map(u => u.name); // NO
// Basic call verificationexpect(mockFunction).toHaveBeenCalled();expect(mockFunction).not.toHaveBeenCalled();// Call countexpect(mockFunction).toHaveBeenCalledTimes(1);expect(mockFunction).toHaveBeenCalledTimes(3);// Called with specific argumentsexpect(mockFunction).toHaveBeenCalledWith('arg1','arg2');expect(mockFunction).toHaveBeenCalledWith(expect.any(String),expect.objectContaining({id: '123'}));// Nth call verificationexpect(mockFunction).toHaveBeenNthCalledWith(1,'first call arg');expect(mockFunction).toHaveBeenNthCalledWith(2,'second call arg');expect(mockFunction).toHaveBeenNthCalledWith(3,'third call arg');// Last call verificationexpect(mockFunction).toHaveBeenLastCalledWith('last arg');
If/Else and Control Flow Testing:
// Test both branchesdescribe('conditional logic',()=>{it('should call functionA when condition is true',()=>{service.process(true);expect(mockFunctionA).toHaveBeenCalled();expect(mockFunctionB).not.toHaveBeenCalled();});it('should call functionB when condition is false',()=>{service.process(false);expect(mockFunctionA).not.toHaveBeenCalled();expect(mockFunctionB).toHaveBeenCalled();});});// Early return/throw verificationit('should not proceed when validation fails',async()=>{awaitexpect(service.process(invalidData)).rejects.toThrow();expect(mockRepository.save).not.toHaveBeenCalled();expect(mockNotificationService.send).not.toHaveBeenCalled();});
Unit Tests
Write tests FIRST before implementation (TDD)
Use Jest as test runner
Follow Arrange-Act-Assert (AAA) pattern
Test file naming: *.spec.ts or *.test.ts
Mock all external dependencies (databases, APIs, services)
Aim for >80% code coverage, 100% for critical logic
NEVER use 'as' or 'any' - always use createMock
ALWAYS use moment.utc() for dates
ALWAYS use Joi for validation
ALWAYS use lodash for array/object operations
What to Assert:
Data equality - Verify returned values match expectations
Function calls - Verify dependencies were called correctly
Call counts - Verify functions called expected number of times
Call arguments - Verify functions called with correct parameters
Call order - Use toHaveBeenNthCalledWith() for sequence verification
Negative cases - Verify functions NOT called in else branches or early returns
Error handling - Verify proper error throwing and handling
Integration Tests
Write after unit tests are passing
Test API endpoints with supertest
Test database operations with test containers
Verify service-to-service communication
Use separate test databases
Clean up test data after each test
E2E Tests
Test complete user workflows
Run against staging environment
Verify Kubernetes deployments
Test frontend-backend integration
Testing Best Practices (TDD)
Write the test name first (describes expected behavior)
Keep tests isolated and independent
Use descriptive test names (should/when/given)
Don't test implementation details
Test behavior, not implementation
Refactor tests along with production code
Run full test suite before committing
Error Handling
Always handle errors explicitly
Use custom error classes with proper error codes
Log errors with context (correlation IDs)
Return consistent error response format
Don't expose internal errors to clients
Security Guidelines
Validate all input data
Sanitize user inputs
Use parameterized queries (avoid SQL injection)
Implement rate limiting
Use HTTPS in all non-local environments
Keep dependencies updated
Never commit secrets or API keys
Performance Considerations
Use connection pooling for databases
Implement caching strategies (Redis)
Optimize database queries (add indexes)
Use CDN for static assets
Implement pagination for large datasets
Use streaming for large file transfers
Documentation
Add JSDoc comments for public APIs
Document complex business logic
Keep README files updated in each package
Document environment variables needed
Include architecture diagrams in docs folder
AI Assistance Preferences
When Suggesting Code (TDD Approach)
IMPORTANT: Always follow TDD - tests come first!
First: Provide the test(s)
Then: Provide the implementation
Finally: Suggest refactoring if needed
Example Response Format:
// Step 1: Write the test (RED)describe('utils',()=>{describe('#calculateTotal',()=>{it('should sum array of numbers',()=>{constnumbers: number[]=[1,2,3];constresult: number=calculateTotal(numbers);expect(result).toBe(6);});});});// Step 2: Minimal implementation (GREEN)functioncalculateTotal(numbers: number[]): number{consttotal: number=_.sum(numbers);// Use lodash, not nativereturntotal;}// Step 3: Refactor (if needed)// Implementation is already clean, no refactoring needed
Code Suggestion Guidelines:
Always show tests before implementation
ALWAYS use explicit type declarations for ALL variables
Use TypeScript types (never 'as' or 'any')
Include error handling
Add relevant comments for complex logic
Consider the Nx workspace structure
Include both unit and integration tests when relevant
Show the TDD cycle: Red → Green → Refactor
Use lodash for all array/object operations
Use moment.utc() for all dates
Use createMock for all test mocks
When Creating New Features
Follow this TDD workflow:
Write failing test for simplest case
Write minimal code to pass
Write test for next case
Refactor when tests pass
Repeat until feature complete
When Debugging Issues
Start by writing a failing test that reproduces the bug
Fix the implementation
Verify the test now passes
Add additional tests for edge cases
When Refactoring
Ensure all existing tests pass first
Don't change tests and code simultaneously
Refactor in small steps
Run tests after each change
Keep tests green throughout
When Reviewing Code
Check for type safety issues
Verify proper error handling
Ensure consistency with existing patterns
Flag security concerns
Suggest performance improvements
When Creating New Files
Follow the Nx workspace structure
Include proper imports and exports
Add basic tests
Include necessary configuration files
Update relevant documentation
When Working with Kubernetes
Provide complete YAML manifests
Include resource limits
Add health check probes
Consider all environments (local/dev/staging/prod)
Include skaffold.yaml updates if needed
Common Commands
# Nx Commands
nx serve <app-name>
nx test<app-name># Run tests
nx test<app-name> --watch # TDD mode (watch for changes)
nx test<app-name> --coverage # Generate coverage report
nx affected:test # Test affected projects
nx build <app-name>
nx dep-graph
# TDD Workflow Commands
npm test -- --watch # Watch mode for TDD
npm test -- --coverage # Check coverage
npm test -- --verbose # Detailed test output# Skaffold Commands
skaffold dev --port-forward
skaffold run -p dev
skaffold delete
# Kubernetes Commands
kubectl get pods -n <namespace>
kubectl logs -f <pod-name>
kubectl describe pod <pod-name>
kubectl apply -f <manifest.yaml>
minikube start