Naming & Folder Conventions
Overview
มาตรฐาน naming conventions สำหรับ files, folders, variables, และ functions ที่ทำให้ codebase predictable และ searchable ลดเวลาในการหา และลดความสับสน
Why This Matters
- Predictability: Guess ได้ว่าไฟล์ชื่ออะไร อยู่ไหน
- Searchability: Find/grep ได้ง่าย
- Consistency: ทุกคนใช้ pattern เดียวกัน
- Maintainability: เข้าใจ code จาก naming
Core Concepts
1. File Naming
- ใช้ casing ตามชนิดไฟล์:
PascalCase.tsx (components), camelCase.ts (utilities), kebab-case.ts (scripts/configs)
- suffix ที่สื่อบทบาท:
*.service.ts, *.controller.ts, *.repository.ts, *.middleware.ts, *.dto.ts, *.types.ts
- หลีกเลี่ยงชื่อกว้าง:
helpers.ts, utils.ts, data.ts (แยกให้เฉพาะเจาะจง)
2. Folder Naming
- โฟลเดอร์ระดับ “feature/entity” ใช้ plural เช่น
users/, orders/
- โฟลเดอร์ “cross-cutting collections” ใช้ plural เช่น
utils/, types/, constants/
- จำกัดความลึก (เช่นไม่เกิน 4 levels) และห้ามตั้ง
misc/, stuff/
3. Variable Naming
camelCase สำหรับตัวแปรทั่วไป, SCREAMING_SNAKE_CASE สำหรับ constants
- boolean ใช้ prefix:
is/has/can/should
- หลีกเลี่ยงตัวย่อที่ไม่เป็นมาตรฐาน; ชื่อควรสื่อ domain (
billingCycle, invoiceId)
4. Function Naming
- ใช้ verb + object:
getUserById, createOrder, validateEmail
- แยกคำให้ชัดเจนเมื่อมี side effects:
enqueueEmail, persistInvoice, publishEvent
- หลีกเลี่ยง
process, handle, doThing ถ้าไม่บอกความหมายจริง
5. Type/Interface Naming
PascalCase สำหรับ types/interfaces/enums
- suffix แบบมาตรฐาน:
UserDto, CreateOrderRequest, PaymentResponse
- ระวัง prefix
I (optional) ให้เลือกแนวเดียวทั้ง repo
6. Test File Naming
- mirror path ของ source และใช้
.test.ts (unit) / .spec.ts (integration) ตามที่ทีมกำหนด
- ใช้ชื่อ describe/it แบบ “what/when/expect” เพื่อให้ output อ่านง่าย
7. Config File Naming
- config ที่ root ใช้ชื่อมาตรฐาน:
.env.example, tsconfig.json, eslint.config.*, jest.config.*
- configs แบบ env-specific ให้ชัด:
config/default.ts, config/production.ts
8. Documentation Naming
- ใช้
README.md ในระดับ repo และในโฟลเดอร์ใหญ่ที่จำเป็น
- ADR ใช้รูปแบบ
docs/adr/NNN-title.md (ลำดับ + ชื่อสั้น)
- runbooks ใช้
docs/runbooks/<topic>.md และมี INDEX.md รวม
Quick Start
# Rules (short):
# - Avoid generic names (helpers, misc, data)
# - Use role suffixes (*.service.ts, *.controller.ts, *.repository.ts)
# - camelCase for code, PascalCase for types, UPPER_CASE for constants
# - Mirror source path in tests (tests/ mirrors src/)
Production Checklist
File Naming Conventions
TypeScript/JavaScript:
├── PascalCase.tsx # React components
├── camelCase.ts # Utilities, services
├── kebab-case.ts # Config, scripts
├── UPPERCASE.md # Documentation
Suffixes:
├── *.service.ts # Service classes
├── *.controller.ts # API controllers
├── *.repository.ts # Data access
├── *.middleware.ts # Middleware
├── *.dto.ts # Data transfer objects
├── *.types.ts # Type definitions
├── *.test.ts # Unit tests
├── *.spec.ts # Integration tests
├── *.e2e.ts # End-to-end tests
├── *.mock.ts # Mock implementations
├── *.config.ts # Configuration
├── *.constants.ts # Constants
Folder Naming Conventions
Structure:
src/
├── api/ # Singular domain folders
├── domain/
│ ├── users/ # Feature/entity folders
│ ├── orders/
│ └── products/
├── infrastructure/
├── shared/
│ ├── utils/ # Utility folders
│ ├── types/
│ └── constants/
└── __tests__/ # Test folders (double underscore)
Rules:
- Use kebab-case for folders
- Singular for domains (user not users)
- Plural for collections (utils, types)
- Max 4 levels deep
- No generic names (helpers, misc, stuff)
Variable & Function Naming
// Variables: camelCase
const userId = 'user123';
const isActive = true;
const userList = [];
// Constants: SCREAMING_SNAKE_CASE
const MAX_RETRY_COUNT = 3;
const API_BASE_URL = 'https://api.example.com';
// Functions: camelCase with verb prefix
function getUserById(id: string) {}
function createUser(data: UserInput) {}
function validateEmail(email: string) {}
function handleUserUpdate(event: Event) {}
// Boolean: is/has/can/should prefix
function isValid(input: string) {}
function hasPermission(user: User) {}
function canDelete(item: Item) {}
// Async: consider suffix
function fetchUserAsync() {}
function getUserPromise() {}
Type & Interface Naming
// Interfaces: PascalCase with 'I' prefix (optional)
interface User {}
interface IUserRepository {} // Optional I prefix
// Types: PascalCase
type UserId = string;
type UserRole = 'admin' | 'user';
// Enums: PascalCase, members SCREAMING_SNAKE_CASE
enum UserStatus {
ACTIVE = 'active',
INACTIVE = 'inactive',
PENDING = 'pending',
}
// DTOs: PascalCase with Dto suffix
interface CreateUserDto {}
interface UpdateUserDto {}
// Response/Request types
interface GetUserResponse {}
interface CreateUserRequest {}
Test Naming
// Test files: mirror source path
// src/services/UserService.ts
// → tests/services/UserService.test.ts
// Test names: describe what, when, expect
describe('UserService', () => {
describe('createUser', () => {
it('should create user when valid data provided', () => {});
it('should throw error when email invalid', () => {});
it('should hash password before saving', () => {});
});
});
// Test helpers: .mock.ts, .fixture.ts
// tests/__mocks__/UserService.mock.ts
// tests/__fixtures__/users.fixture.ts
Anti-patterns
| Bad |
Good |
Why |
data.ts |
users.ts |
Generic = confusing |
helpers.ts |
string-utils.ts |
Vague = hard to find |
index.ts (many) |
users.service.ts |
Barrel = debugging nightmare |
get() |
getUserById() |
No context |
temp_var |
processingQueue |
Meaningless |
MyClass2 |
ExtendedMyClass |
Numbers = code smell |
Enforcement
// .eslintrc.js
{
"rules": {
"@typescript-eslint/naming-convention": [
"error",
{ "selector": "variable", "format": ["camelCase", "UPPER_CASE"] },
{ "selector": "function", "format": ["camelCase"] },
{ "selector": "typeLike", "format": ["PascalCase"] }
]
}
}
Integration Points
- ESLint/Prettier configs
- Pre-commit hooks
- Code review checklists
- IDE snippets
Further Reading
1---2name: naming-folder-conventions3description: Conventions for naming files, folders, variables, functions, types, tests, and configs to keep a codebase predictable, searchable, and consistent4---5
6# Naming & Folder Conventions
7
8## Overview
9
10มาตรฐาน naming conventions สำหรับ files, folders, variables, และ functions ที่ทำให้ codebase predictable และ searchable ลดเวลาในการหา และลดความสับสน
11
12## Why This Matters
13
14- **Predictability**: Guess ได้ว่าไฟล์ชื่ออะไร อยู่ไหน
15- **Searchability**: Find/grep ได้ง่าย
16- **Consistency**: ทุกคนใช้ pattern เดียวกัน
17- **Maintainability**: เข้าใจ code จาก naming
18
19---
20
21## Core Concepts
22
23### 1. File Naming
24
25- ใช้ casing ตามชนิดไฟล์: `PascalCase.tsx` (components), `camelCase.ts` (utilities), `kebab-case.ts` (scripts/configs)
26- suffix ที่สื่อบทบาท: `*.service.ts`, `*.controller.ts`, `*.repository.ts`, `*.middleware.ts`, `*.dto.ts`, `*.types.ts`
27- หลีกเลี่ยงชื่อกว้าง: `helpers.ts`, `utils.ts`, `data.ts` (แยกให้เฉพาะเจาะจง)
28
29### 2. Folder Naming
30
31- โฟลเดอร์ระดับ “feature/entity” ใช้ plural เช่น `users/`, `orders/`
32- โฟลเดอร์ “cross-cutting collections” ใช้ plural เช่น `utils/`, `types/`, `constants/`
33- จำกัดความลึก (เช่นไม่เกิน 4 levels) และห้ามตั้ง `misc/`, `stuff/`
34
35### 3. Variable Naming
36
37- `camelCase` สำหรับตัวแปรทั่วไป, `SCREAMING_SNAKE_CASE` สำหรับ constants
38- boolean ใช้ prefix: `is/has/can/should`
39- หลีกเลี่ยงตัวย่อที่ไม่เป็นมาตรฐาน; ชื่อควรสื่อ domain (`billingCycle`, `invoiceId`)
40
41### 4. Function Naming
42
43- ใช้ verb + object: `getUserById`, `createOrder`, `validateEmail`
44- แยกคำให้ชัดเจนเมื่อมี side effects: `enqueueEmail`, `persistInvoice`, `publishEvent`
45- หลีกเลี่ยง `process`, `handle`, `doThing` ถ้าไม่บอกความหมายจริง
46
47### 5. Type/Interface Naming
48
49- `PascalCase` สำหรับ types/interfaces/enums
50- suffix แบบมาตรฐาน: `UserDto`, `CreateOrderRequest`, `PaymentResponse`
51- ระวัง prefix `I` (optional) ให้เลือกแนวเดียวทั้ง repo
52
53### 6. Test File Naming
54
55- mirror path ของ source และใช้ `.test.ts` (unit) / `.spec.ts` (integration) ตามที่ทีมกำหนด
56- ใช้ชื่อ describe/it แบบ “what/when/expect” เพื่อให้ output อ่านง่าย
57
58### 7. Config File Naming
59
60- config ที่ root ใช้ชื่อมาตรฐาน: `.env.example`, `tsconfig.json`, `eslint.config.*`, `jest.config.*`
61- configs แบบ env-specific ให้ชัด: `config/default.ts`, `config/production.ts`
62
63### 8. Documentation Naming
64
65- ใช้ `README.md` ในระดับ repo และในโฟลเดอร์ใหญ่ที่จำเป็น
66- ADR ใช้รูปแบบ `docs/adr/NNN-title.md` (ลำดับ + ชื่อสั้น)
67- runbooks ใช้ `docs/runbooks/<topic>.md` และมี `INDEX.md` รวม
68
69## Quick Start
70
71```markdown
72# Rules (short):
73# - Avoid generic names (helpers, misc, data)
74# - Use role suffixes (*.service.ts, *.controller.ts, *.repository.ts)
75# - camelCase for code, PascalCase for types, UPPER_CASE for constants
76# - Mirror source path in tests (tests/ mirrors src/)
77```
78
79## Production Checklist
80
81- [ ] Naming convention documented
82- [ ] Enforced via linter
83- [ ] Team trained on conventions
84- [ ] Reviewed in code reviews
85- [ ] Auto-fix where possible
86
87## File Naming Conventions
88
89```
90TypeScript/JavaScript:
91├── PascalCase.tsx # React components
92├── camelCase.ts # Utilities, services
93├── kebab-case.ts # Config, scripts
94├── UPPERCASE.md # Documentation
95
96Suffixes:
97├── *.service.ts # Service classes
98├── *.controller.ts # API controllers
99├── *.repository.ts # Data access
100├── *.middleware.ts # Middleware
101├── *.dto.ts # Data transfer objects
102├── *.types.ts # Type definitions
103├── *.test.ts # Unit tests
104├── *.spec.ts # Integration tests
105├── *.e2e.ts # End-to-end tests
106├── *.mock.ts # Mock implementations
107├── *.config.ts # Configuration
108├── *.constants.ts # Constants
109```
110
111## Folder Naming Conventions
112
113```
114Structure:
115src/
116├── api/ # Singular domain folders
117├── domain/
118│ ├── users/ # Feature/entity folders
119│ ├── orders/
120│ └── products/
121├── infrastructure/
122├── shared/
123│ ├── utils/ # Utility folders
124│ ├── types/
125│ └── constants/
126└── __tests__/ # Test folders (double underscore)
127
128Rules:
129- Use kebab-case for folders
130- Singular for domains (user not users)
131- Plural for collections (utils, types)
132- Max 4 levels deep
133- No generic names (helpers, misc, stuff)
134```
135
136## Variable & Function Naming
137
138```typescript
139// Variables: camelCase
140const userId = 'user123';
141const isActive = true;
142const userList = [];
143
144// Constants: SCREAMING_SNAKE_CASE
145const MAX_RETRY_COUNT = 3;
146const API_BASE_URL = 'https://api.example.com';
147
148// Functions: camelCase with verb prefix
149function getUserById(id: string) {}
150function createUser(data: UserInput) {}
151function validateEmail(email: string) {}
152function handleUserUpdate(event: Event) {}
153
154// Boolean: is/has/can/should prefix
155function isValid(input: string) {}
156function hasPermission(user: User) {}
157function canDelete(item: Item) {}
158
159// Async: consider suffix
160function fetchUserAsync() {}
161function getUserPromise() {}
162```
163
164## Type & Interface Naming
165
166```typescript
167// Interfaces: PascalCase with 'I' prefix (optional)
168interface User {}
169interface IUserRepository {} // Optional I prefix
170
171// Types: PascalCase
172type UserId = string;
173type UserRole = 'admin' | 'user';
174
175// Enums: PascalCase, members SCREAMING_SNAKE_CASE
176enum UserStatus {
177 ACTIVE = 'active',
178 INACTIVE = 'inactive',
179 PENDING = 'pending',
180}
181
182// DTOs: PascalCase with Dto suffix
183interface CreateUserDto {}
184interface UpdateUserDto {}
185
186// Response/Request types
187interface GetUserResponse {}
188interface CreateUserRequest {}
189```
190
191## Test Naming
192
193```typescript
194// Test files: mirror source path
195// src/services/UserService.ts
196// → tests/services/UserService.test.ts
197
198// Test names: describe what, when, expect
199describe('UserService', () => {
200 describe('createUser', () => {
201 it('should create user when valid data provided', () => {});
202 it('should throw error when email invalid', () => {});
203 it('should hash password before saving', () => {});
204 });
205});
206
207// Test helpers: .mock.ts, .fixture.ts
208// tests/__mocks__/UserService.mock.ts
209// tests/__fixtures__/users.fixture.ts
210```
211
212## Anti-patterns
213
214| Bad | Good | Why |
215|-----|------|-----|
216| `data.ts` | `users.ts` | Generic = confusing |
217| `helpers.ts` | `string-utils.ts` | Vague = hard to find |
218| `index.ts` (many) | `users.service.ts` | Barrel = debugging nightmare |
219| `get()` | `getUserById()` | No context |
220| `temp_var` | `processingQueue` | Meaningless |
221| `MyClass2` | `ExtendedMyClass` | Numbers = code smell |
222
223## Enforcement
224
225```json
226// .eslintrc.js
227{
228 "rules": {
229 "@typescript-eslint/naming-convention": [
230 "error",
231 { "selector": "variable", "format": ["camelCase", "UPPER_CASE"] },
232 { "selector": "function", "format": ["camelCase"] },
233 { "selector": "typeLike", "format": ["PascalCase"] }
234 ]
235 }
236}
237```
238
239## Integration Points
240
241- ESLint/Prettier configs
242- Pre-commit hooks
243- Code review checklists
244- IDE snippets
245
246## Further Reading
247
248- [Naming Cheatsheet](https://github.com/kettanaito/naming-cheatsheet)
249- [Clean Code Naming](https://cleancoders.com/episode/clean-code-episode-2)