Coding Standards Overview
Why Coding Standards Matter
Code quality is a team responsibility. Coding standards help us maintain consistency, improve readability, and make our codebase more maintainable. They also reduce the cognitive load when switching between projects, as engineers can expect similar patterns and structures.
Good coding standards provide the following benefits:
- Improved code quality: Consistent code is easier to review, test, and maintain
- Reduced onboarding time: New team members can get up to speed faster
- Better collaboration: Standardized approaches make it easier to work together
- Reduced technical debt: Well-structured code is easier to refactor and extend
- Fewer bugs: Clear patterns help prevent common errors
Our Technology Stack
Kirana Labs primarily uses the following technologies:
- Frontend: TypeScript with NextJS 15 (React Server Components)
- CMS: PayloadCMS v3.24
- Backend: Node.js v20 (for NextJS), Django v5 (for some projects)
- Database: PostgreSQL (latest)
- Caching/Queues: Redis, BullMQ
- Infrastructure: Railway, with some projects on AWS, Google Cloud, or Azure
- Version Control: GitHub
- CI/CD: GitHub Actions
TypeScript Coding Standards
Type Definitions
- Always use explicit type definitions for function parameters and return values
- Use interfaces for object shapes that will be used in multiple places
- Use type aliases for complex types or union types
- Avoid using
anytype - preferunknownif the type is truly not known - Use generics to create reusable components and functions
// Good
interface User {
id: string;
name: string;
email: string;
}
function getUserById(id: string): Promise<User | null> {
// implementation
}
// Avoid
function getUserById(id): any {
// implementation
}
File and Directory Structure
For NextJS projects:
src/
├── app/ # Next.js 15 app directory
│ ├── api/ # API routes
│ ├── (auth)/ # Auth-related routes in route group
│ ├── [param]/ # Dynamic routes
│ └── layout.tsx # Root layout
├── components/ # Shared components
│ ├── ui/ # Reusable UI components
│ ├── forms/ # Form-related components
│ └── layout/ # Layout components
├── lib/ # Utility functions and shared code
├── hooks/ # Custom React hooks
├── types/ # TypeScript type definitions
├── utils/ # Utility functions
└── styles/ # Global styles
Naming Conventions
- Files and Directories: Use kebab-case for file and directory names (e.g.,
user-profile.tsx) - Components: Use PascalCase for component names (e.g.,
UserProfile) - Functions: Use camelCase for function names (e.g.,
getUserProfile) - Interfaces and Types: Use PascalCase prefixed with
Ifor interfaces (e.g.,IUserProfile) andTfor types (e.g.,TUserRole) - Variables: Use camelCase for variable names (e.g.,
userProfile) - Constants: Use UPPER_SNAKE_CASE for constants (e.g.,
MAX_FILE_SIZE)
Code Formatting
We use Biome for formatting and ESLint for linting. The default configuration should be used for all projects.
- Maximum line length: 100 characters
- Indentation: 2 spaces
- Semicolons: Required
- Quotes: Single quotes for strings
- Trailing commas: Required for multiline
Component Structure
- Use functional components with hooks
- Separate UI logic from business logic
- Use React Server Components where possible to improve performance
- Implement proper error boundaries
// Good example of a React component
import { useState, useEffect } from 'react';
import { fetchUserData } from '@/lib/api';
import { IUser } from '@/types';
import LoadingSpinner from '@/components/ui/loading-spinner';
import ErrorMessage from '@/components/ui/error-message';
interface UserProfileProps {
userId: string;
}
export default function UserProfile({ userId }: UserProfileProps) {
const [user, setUser] = useState<IUser | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function loadUser() {
try {
setLoading(true);
const userData = await fetchUserData(userId);
setUser(userData);
} catch (err) {
setError('Failed to load user data');
console.error(err);
} finally {
setLoading(false);
}
}
loadUser();
}, [userId]);
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage message={error} />;
if (!user) return <ErrorMessage message="User not found" />;
return (
<div className="user-profile">
<h1>{user.name}</h1>
<p>{user.email}</p>
{/* Additional user information */}
</div>
);
}
Comments and Documentation
- Add JSDoc comments for all functions and components that aren't self-explanatory:
/**
* Fetches user data from the API
*
* @param {string} userId - The ID of the user to fetch
* @returns {Promise<IUser>} A promise that resolves to the user object
* @throws {Error} If the user cannot be found or the request fails
*/
async function fetchUserData(userId: string): Promise<IUser> {
// implementation
}
- Use inline comments to explain complex logic or business rules
- Keep comments up to date when code changes
- Comment "why" not "what" - the code should be readable enough to understand what it's doing
Error Handling
- Always handle errors appropriately
- Use try/catch blocks for async code
- Log errors with enough context to debug
- Provide user-friendly error messages
- Implement global error handling for API requests
Performance Considerations
- Use React Server Components for data fetching and rendering where possible
- Implement proper caching strategies using NextJS built-in caching
- Minimize client-side JavaScript
- Optimize images and assets
- Use code splitting to reduce initial load times
PayloadCMS Standards
- Keep collection definitions in separate files
- Implement access control consistently across collections
- Use hooks for complex business logic
- Document field validation rules
- Implement proper indexing for frequently queried fields
Testing Standards
- Write unit tests for complex functions and utilities
- Create integration tests for critical user flows
- Implement end-to-end tests for core features
- Follow the AAA (Arrange, Act, Assert) pattern in tests
- Mock external dependencies appropriately
Git Commit Standards
- Write clear, concise commit messages
- Start commit messages with a verb in the present tense (e.g., "Add user profile component")
- Reference ticket numbers in commit messages (e.g., "KL-123: Add user profile component")
- Keep commits small and focused on a single change
- Squash commits before merging to maintain a clean history
Next Steps
Refer to the language-specific sections for more detailed guidelines: