- Naming Conventions
- SOLID Principles
- OOP Best Practices
- TypeScript Guidelines
- React Components Guidelines
- File Organization
- MUST start with
Iprefix - MUST be PascalCase
// ✅ Correct
interface IUser {
Id: number;
Name: string;
}
// ❌ Wrong
interface User {
id: number;
name: string;
}- MUST start with
Tprefix - MUST be PascalCase
// ✅ Correct
type TUserRole = 'admin' | 'developer' | 'viewer';
// ❌ Wrong
type UserRole = 'admin' | 'developer' | 'viewer';- MUST start with
Eprefix - MUST be PascalCase
- Members MUST be PascalCase
// ✅ Correct
enum EProjectType {
Node = 'node',
React = 'react',
Static = 'static',
Docker = 'docker',
NextJS = 'next',
Other = 'other',
}
// ❌ Wrong
enum ProjectType {
nodejs = 'nodejs',
react = 'react',
}- MUST be PascalCase
- No prefix required
// ✅ Correct
class ProjectService {
private apiUrl: string;
constructor(apiUrl: string) {
this.apiUrl = apiUrl;
}
public async GetProjects(): Promise<IProject[]> {
// Implementation
}
}
// ❌ Wrong
class projectService {
get_projects() {
// Implementation
}
}- MUST be PascalCase
- MUST use arrow functions for functional components
// ✅ Correct
export const ProjectsPage: React.FC = () => {
return <div>Projects</div>;
};
// ❌ Wrong
export const projectsPage = () => {
return <div>Projects</div>;
};- SHOULD be PascalCase for public methods
- CAN be camelCase for private/utility functions
// ✅ Correct
class UserService {
public async GetUserById(id: number): Promise<IUser> {
return this.fetchUser(id);
}
private async fetchUser(id: number): Promise<IUser> {
// Implementation
}
}
// Handler functions in React
const HandleSubmit = (event: React.FormEvent) => {
event.preventDefault();
};- PascalCase for React state and component-level variables
- camelCase for utility variables and loop counters
- UPPER_CASE for constants
// ✅ Correct - React Component
const [FormData, setFormData] = useState<IFormData>({});
const [Loading, setLoading] = useState(false);
// ✅ Correct - Constants
const API_BASE_URL = 'https://api.example.com';
const MAX_RETRY_ATTEMPTS = 3;
// ✅ Correct - Utility
const fetchData = async () => {
const response = await fetch(API_BASE_URL);
return response.json();
};- MUST be PascalCase to match backend API
// ✅ Correct
interface IProject {
Id: number;
Name: string;
RepoUrl: string;
CreatedAt: Date;
}
// ❌ Wrong
interface IProject {
id: number;
name: string;
repoUrl: string;
createdAt: Date;
}- Each class/function should have ONE reason to change
- Max 500 lines per file
- Max 100 lines per function
// ✅ Correct - Single Responsibility
class ProjectService {
public async GetAll(): Promise<IProject[]> {
return ApiInstance.get('/projects');
}
}
class ProjectValidator {
public Validate(project: IProject): boolean {
return project.Name.length > 0 && project.RepoUrl.length > 0;
}
}
// ❌ Wrong - Multiple Responsibilities
class ProjectManager {
public async GetAll(): Promise<IProject[]> { }
public Validate(project: IProject): boolean { }
public RenderUI(project: IProject): JSX.Element { }
public SendEmail(project: IProject): void { }
}- Open for extension, closed for modification
- Use interfaces and abstract classes
// ✅ Correct
interface INotificationService {
Send(message: string): Promise<void>;
}
class DiscordNotificationService implements INotificationService {
public async Send(message: string): Promise<void> {
// Discord-specific implementation
}
}
class SlackNotificationService implements INotificationService {
public async Send(message: string): Promise<void> {
// Slack-specific implementation
}
}- Derived classes must be substitutable for their base classes
// ✅ Correct
abstract class BaseService {
protected abstract GetEndpoint(): string;
public async FetchData<T>(): Promise<T> {
const endpoint = this.GetEndpoint();
return ApiInstance.get(endpoint);
}
}
class ProjectService extends BaseService {
protected GetEndpoint(): string {
return '/projects';
}
}- Clients should not depend on interfaces they don't use
- Prefer small, focused interfaces
// ✅ Correct - Small, focused interfaces
interface IReadable {
Read(): Promise<void>;
}
interface IWritable {
Write(data: any): Promise<void>;
}
interface IDeletable {
Delete(id: number): Promise<void>;
}
// ❌ Wrong - Fat interface
interface IRepository {
Read(): Promise<void>;
Write(data: any): Promise<void>;
Delete(id: number): Promise<void>;
Export(): void;
Import(): void;
Backup(): void;
}- Depend on abstractions, not concretions
// ✅ Correct
interface IApiClient {
Get<T>(url: string): Promise<T>;
Post<T>(url: string, data: any): Promise<T>;
}
class ProjectService {
constructor(private apiClient: IApiClient) {}
public async GetProjects(): Promise<IProject[]> {
return this.apiClient.Get<IProject[]>('/projects');
}
}
// ❌ Wrong - Direct dependency
class ProjectService {
public async GetProjects(): Promise<IProject[]> {
return axios.get('/projects'); // Direct dependency on axios
}
}class ExampleService {
// 1. Static fields
private static instance: ExampleService;
// 2. Instance fields
private apiUrl: string;
protected cache: Map<string, any>;
public isReady: boolean;
// 3. Constructor
constructor(apiUrl: string) {
this.apiUrl = apiUrl;
this.cache = new Map();
this.isReady = false;
}
// 4. Static methods
public static GetInstance(): ExampleService {
if (!ExampleService.instance) {
ExampleService.instance = new ExampleService('');
}
return ExampleService.instance;
}
// 5. Public methods
public async Initialize(): Promise<void> {
this.isReady = true;
}
// 6. Protected methods
protected ValidateData(data: any): boolean {
return data !== null;
}
// 7. Private methods
private async fetchFromApi(): Promise<any> {
// Implementation
}
}- Use
public,protected,privatemodifiers - Prefer getters/setters over direct property access
// ✅ Correct
class User {
private _name: string;
public get Name(): string {
return this._name;
}
public set Name(value: string) {
if (value.length < 3) {
throw new Error('Name too short');
}
this._name = value;
}
}
// ❌ Wrong
class User {
public name: string; // Direct access, no validation
}- Use inheritance for "is-a" relationships
- Use composition for "has-a" relationships
// ✅ Correct - Inheritance
abstract class BaseComponent {
protected abstract Render(): JSX.Element;
}
class ProjectCard extends BaseComponent {
protected Render(): JSX.Element {
return <div>Project Card</div>;
}
}
// ✅ Correct - Composition
class ProjectService {
constructor(
private apiClient: IApiClient,
private logger: ILogger,
private cache: ICache
) {}
}// ✅ Correct - Explicit types
function CalculateTotal(items: ICartItem[]): number {
return items.reduce((sum, item) => sum + item.Price, 0);
}
// ❌ Wrong - No types
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}// ✅ Correct
function HandleError(error: Error | unknown): void {
if (error instanceof Error) {
console.error(error.message);
}
}
// ⚠️ Acceptable for gradual migration (will warn)
function ProcessData(data: any): void {
// Temporary, should be replaced with proper type
}// ✅ Correct
const username = user.Name ?? 'Guest';
// ❌ Wrong
const username = user.Name || 'Guest'; // Fails for empty string// ✅ Correct
const city = user?.Address?.City;
// ❌ Wrong
const city = user && user.Address && user.Address.City;// ✅ Correct
interface IProjectCardProps {
Project: IProject;
OnDelete: (id: number) => void;
}
export const ProjectCard: React.FC<IProjectCardProps> = ({ Project, OnDelete }) => {
const [Loading, setLoading] = useState(false);
const HandleDelete = async () => {
setLoading(true);
await OnDelete(Project.Id);
setLoading(false);
};
return (
<Card>
<Typography>{Project.Name}</Typography>
<Button onClick={HandleDelete} disabled={Loading}>
Delete
</Button>
</Card>
);
};// ✅ Correct
interface IProjectListProps {
ProjectId: number;
}
interface IProjectListState {
Projects: IProject[];
Loading: boolean;
}
export class ProjectList extends React.Component<IProjectListProps, IProjectListState> {
public constructor(props: IProjectListProps) {
super(props);
this.state = {
Projects: [],
Loading: false,
};
}
public async componentDidMount(): Promise<void> {
await this.FetchProjects();
}
private async FetchProjects(): Promise<void> {
this.setState({ Loading: true });
const projects = await ProjectsService.GetAll();
this.setState({ Projects: projects, Loading: false });
}
public render(): JSX.Element {
const { Projects, Loading } = this.state;
if (Loading) {
return <CircularProgress />;
}
return (
<div>
{Projects.map(project => (
<ProjectCard key={project.Id} Project={project} />
))}
</div>
);
}
}src/
├── components/ # Reusable UI components
│ ├── Layout/
│ │ ├── Layout.tsx
│ │ ├── Sidebar.tsx
│ │ └── Navbar.tsx
│ └── Projects/
│ ├── ProjectCard.tsx
│ └── Wizard/
│ ├── ProjectWizard.tsx
│ ├── Step1BasicInfo.tsx
│ └── Step2Configuration.tsx
├── pages/ # Page components
│ ├── Auth/
│ │ ├── LoginPage.tsx
│ │ └── RegisterPage.tsx
│ └── Projects/
│ ├── ProjectsPage.tsx
│ └── ProjectDetailsPage.tsx
├── services/ # API services (Classes)
│ ├── ApiClient.ts
│ ├── ProjectsService.ts
│ └── DeploymentsService.ts
├── contexts/ # React Contexts
│ ├── AuthContext.tsx
│ ├── ThemeContext.tsx
│ └── LanguageContext.tsx
├── types/ # TypeScript definitions
│ └── index.ts
├── utils/ # Utility functions
│ ├── formatters.ts
│ └── validators.ts
└── constants/ # Constants
└── index.ts
- Components: PascalCase (e.g.,
ProjectCard.tsx) - Services: PascalCase (e.g.,
ProjectsService.ts) - Utilities: camelCase (e.g.,
formatters.ts) - Constants: camelCase (e.g.,
apiEndpoints.ts)
- All interfaces start with
I - All type aliases start with
T - All enums start with
E - No
anytypes (or justified with comment) - All functions have explicit return types
- PascalCase used for all public APIs
- Classes follow member ordering rules
- No circular dependencies
- Max file length: 500 lines
- Max function length: 100 lines
- ESLint passes with zero errors
- Naming: Does it follow PascalCase conventions?
- SOLID: Does each class/function have a single responsibility?
- Types: Are all types explicit and correct?
- Reusability: Can this code be reused elsewhere?
- Testability: Can this code be easily tested?
- Performance: Are there any obvious performance issues?
- Security: Are there any security vulnerabilities?
Last Updated: 2025-11-28