Skip to content

Code Standards: TypeScript Guidelines, File Structure & Error Codes

What You'll Learn

  • ✅ Understand the coding standards that AI App Factory generated code must follow
  • ✅ Master TypeScript types and naming conventions
  • ✅ Learn the file structure and organization of frontend and backend code
  • ✅ Understand how to use the unified error code system

Core Concept

AI App Factory generated code must follow unified coding standards to ensure consistent code quality, maintainability, and readability. These standards cover general formatting, TypeScript type system, frontend/backend architecture design, comment style, and error handling mechanisms.

Why do we need coding standards?

  • Consistency: All Agent-generated code has a unified style, reducing learning costs
  • Maintainability: Clear naming and structure make subsequent modifications easier
  • Readability: Standardized code allows team members to quickly understand intentions
  • Security: Strict type system and error handling reduce runtime errors

1. General Standards

1.1 Language and Formatting

ItemStandard
LanguageTypeScript (strict mode)
Indentation2 spaces
Line EndingsLF (Unix)
EncodingUTF-8
Max Line Length100 characters (code), 80 characters (comments)
SemicolonsRequired
QuotesSingle quotes (strings), Double quotes (JSX attributes)

1.2 Naming Conventions

TypeStyleExample
Filename (regular)camelCase.tsuserService.ts, apiClient.ts
Filename (component)PascalCase.tsxButton.tsx, HomeScreen.tsx
Filename (test)*.test.ts/tsxuser.test.ts, Button.test.tsx
VariablescamelCaseuserName, isLoading
FunctionscamelCasegetUserById, formatDate
Classes/Interfaces/TypesPascalCaseUser, ApiResponse, CreateItemDto
ConstantsUPPER_SNAKE_CASEAPI_URL, MAX_RETRY_COUNT
Private propertiesNo underscore prefixUse TypeScript private keyword
Boolean variablesis/has/can prefixisActive, hasPermission, canEdit
Event handlershandle prefixhandleClick, handleSubmit
Hooksuse prefixuseItems, useAuth

Naming Tips

Good naming should be self-explanatory, without needing extra comments to explain purpose. For example, isValid is clearer than check, and getUserById is more specific than getData.

1.3 File Organization

Single responsibility per file:

  • One component per file
  • One service per file
  • Related types can be placed in the same types.ts file

Import order:

typescript
// 1. Node.js built-in modules
import path from 'path';

// 2. External dependencies
import express from 'express';
import { z } from 'zod';

// 3. Internal modules (absolute paths)
import { prisma } from '@/lib/prisma';
import { config } from '@/config';

// 4. Relative path imports
import { UserService } from './userService';
import type { User } from './types';

2. TypeScript Standards

2.1 Type Definitions

Prefer interface for object types:

typescript
// ✅ Good
interface User {
  id: number;
  name: string;
  email: string;
}

// ❌ Avoid (unless you need union types or mapped types)
type User = {
  id: number;
  name: string;
  email: string;
};

Define types for all public APIs:

typescript
// ✅ Good
interface CreateUserDto {
  name: string;
  email: string;
}

interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: {
    message: string;
    details?: unknown;
  };
}

function createUser(data: CreateUserDto): Promise<ApiResponse<User>> {
  // ...
}

// ❌ Avoid
function createUser(data: any): Promise<any> {
  // ...
}

2.2 Type Assertions

Prohibit any

Generated code strictly prohibits using any type. Must use unknown and add type guards.

Avoid using any:

typescript
// ✅ Good
function parseJson(text: string): unknown {
  return JSON.parse(text);
}

const data = parseJson(text);
if (isUser(data)) {
  // data is inferred as User
}

// ❌ Avoid
const data: any = JSON.parse(text);

Use type guards:

typescript
// ✅ Good
function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'name' in value
  );
}

// ❌ Avoid
const user = data as User; // Unsafe assertion

2.3 Generics

Use meaningful names for generic parameters:

typescript
// ✅ Good
interface Repository<TEntity> {
  findById(id: number): Promise<TEntity | null>;
  save(entity: TEntity): Promise<TEntity>;
}

// ❌ Avoid
interface Repository<T> {
  findById(id: number): Promise<T | null>;
  save(entity: T): Promise<T>;
}

3. Backend Standards

3.1 Express Routes

Use Router to organize routes:

typescript
// ✅ Good
// src/routes/items.ts
import { Router } from 'express';
import { itemController } from '@/controllers/item';
import { validateRequest } from '@/middleware/validation';
import { createItemSchema, updateItemSchema } from '@/validators/item';

const router = Router();

router.get('/', itemController.list);
router.get('/:id', itemController.getById);
router.post('/', validateRequest(createItemSchema), itemController.create);
router.put('/:id', validateRequest(updateItemSchema), itemController.update);
router.delete('/:id', itemController.remove);

export default router;

RESTful naming:

typescript
// ✅ Good
GET    /api/items         // List
GET    /api/items/:id     // Detail
POST   /api/items         // Create
PUT    /api/items/:id     // Update
DELETE /api/items/:id     // Delete

// ❌ Avoid
GET    /api/getItems
POST   /api/createItem
POST   /api/items/delete/:id

3.2 Controllers

Keep controllers concise:

typescript
// ✅ Good
export const itemController = {
  async list(req: Request, res: Response, next: NextFunction) {
    try {
      const items = await itemService.findAll();
      res.json({ success: true, data: items });
    } catch (error) {
      next(error);
    }
  },

  async create(req: Request, res: Response, next: NextFunction) {
    try {
      const item = await itemService.create(req.body);
      res.status(201).json({ success: true, data: item });
    } catch (error) {
      next(error);
    }
  },
};

3.3 Service Layer

Business logic in service layer:

typescript
// ✅ Good
// src/services/item.ts
export const itemService = {
  async findAll() {
    return prisma.item.findMany({
      orderBy: { createdAt: 'desc' },
    });
  },

  async findById(id: number) {
    const item = await prisma.item.findUnique({ where: { id } });
    if (!item) {
      throw new AppError(404, 'Item not found');
    }
    return item;
  },

  async create(data: CreateItemDto) {
    return prisma.item.create({ data });
  },
};

3.4 Error Handling

Use unified error class:

typescript
// src/lib/errors.ts
export class AppError extends Error {
  constructor(
    public statusCode: number,
    public message: string,
    public details?: unknown
  ) {
    super(message);
    this.name = 'AppError';
  }
}

// Usage
throw new AppError(404, 'Item not found');
throw new AppError(400, 'Validation failed', errors);

Global error handling middleware:

typescript
// src/middleware/errorHandler.ts
export function errorHandler(
  error: Error,
  req: Request,
  res: Response,
  next: NextFunction
) {
  if (error instanceof AppError) {
    return res.status(error.statusCode).json({
      success: false,
      error: {
        message: error.message,
        details: error.details,
      },
    });
  }

  // Hide internal errors in production
  const message =
    process.env.NODE_ENV === 'production'
      ? 'Internal server error'
      : error.message;

  res.status(500).json({
    success: false,
    error: { message },
  });
}

4. Frontend Standards

4.1 Component Structure

Function component format:

tsx
// ✅ Good
interface ButtonProps {
  children: React.ReactNode;
  onPress: () => void;
  loading?: boolean;
  disabled?: boolean;
}

export function Button({
  children,
  onPress,
  loading = false,
  disabled = false,
}: ButtonProps) {
  return (
    <TouchableOpacity
      onPress={onPress}
      disabled={loading || disabled}
      style={styles.container}
    >
      {loading ? (
        <ActivityIndicator />
      ) : (
        <Text style={styles.text}>{children}</Text>
      )}
    </TouchableOpacity>
  );
}

const styles = StyleSheet.create({
  container: {
    // ...
  },
  text: {
    // ...
  },
});

4.2 Hook Standards

Custom hooks return objects:

typescript
// ✅ Good
export function useItems() {
  const [items, setItems] = useState<Item[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const refresh = async () => {
    // ...
  };

  const create = async (data: CreateItemDto) => {
    // ...
  };

  return {
    items,
    loading,
    error,
    refresh,
    create,
  };
}

4.3 Style Standards

Use StyleSheet:

typescript
// ✅ Good
const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
  },
  title: {
    fontSize: 18,
    fontWeight: 'bold',
  },
});

// ❌ Avoid inline styles
<View style={{ flex: 1, padding: 16 }}>

Use theme system:

typescript
// src/styles/theme.ts
export const theme = {
  colors: {
    primary: '#2563eb',
    text: {
      primary: '#1e293b',
      secondary: '#64748b',
    },
    background: '#ffffff',
  },
  spacing: {
    xs: 4,
    sm: 8,
    md: 16,
    lg: 24,
    xl: 32,
  },
  fontSize: {
    sm: 14,
    base: 16,
    lg: 18,
    xl: 20,
  },
};

4.4 Navigation

Type-safe navigation parameters:

typescript
// src/navigation/types.ts
export type RootStackParamList = {
  Home: undefined;
  Detail: { id: number };
  Create: undefined;
};

// Usage
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
navigation.navigate('Detail', { id: 1 });

5. Prisma Standards

5.1 Schema Definition

Model naming uses PascalCase singular:

prisma
// ✅ Good
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

5.2 Query Standards

Use select to limit returned fields:

typescript
// ✅ Good (only return needed fields)
const users = await prisma.user.findMany({
  select: {
    id: true,
    name: true,
    email: true,
  },
});

// ❌ Avoid (return all fields including sensitive information)
const users = await prisma.user.findMany();

6. Comment Standards

6.1 When to Add Comments

Scenarios that need comments:

  • Complex business logic
  • Non-obvious design decisions
  • Reasons for performance optimizations
  • Public API interfaces

Scenarios that don't need comments:

  • Self-explanatory code
  • Simple getter/setter
  • Obvious implementations

6.2 Comment Format

typescript
// ✅ Good - explain why
// Use optimistic updates to improve user experience, rollback on failure
const optimisticUpdate = async (id: number, data: UpdateDto) => {
  const previousData = items;
  setItems(prev => prev.map(item => item.id === id ? { ...item, ...data } : item));

  try {
    await api.update(id, data);
  } catch {
    setItems(previousData); // Rollback
    throw new Error('Update failed');
  }
};

// ❌ Avoid - explain what (code already explains)
// Set items
setItems(newItems);

Comment Principle

Comments should explain why rather than what. Good code should be self-explanatory, and comments only supplement information not directly visible from the code.


7. Error Code Standards

7.1 Error Code Structure

Format definition:

[Business_Module]_[Error_Type]_[Specific_Error]

Example: AUTH_VALIDATION_INVALID_EMAIL

Naming conventions:

  • All uppercase: Use SCREAMING_SNAKE_CASE
  • Business module: 2-4 characters, representing functional module (e.g., AUTH, USER, ITEM)
  • Error type: Generic error type (e.g., VALIDATION, NOT_FOUND, FORBIDDEN)
  • Specific error: Detailed description (optional)

7.2 Standard Error Types

1. Validation Errors (VALIDATION)

HTTP Status Code: 400

Error CodeDescriptionExample Scenario
[MODULE]_VALIDATION_REQUIREDMissing required fieldTitle not provided when creating
[MODULE]_VALIDATION_INVALID_FORMATIncorrect formatEmail format error
[MODULE]_VALIDATION_OUT_OF_RANGEOut of rangeamount < 0 or > 10000
[MODULE]_VALIDATION_DUPLICATEDuplicate valueEmail already exists

Examples:

typescript
AUTH_VALIDATION_REQUIRED       // Missing required field
AUTH_VALIDATION_INVALID_EMAIL  // Email format error
ITEM_VALIDATION_OUT_OF_RANGE   // Amount out of range

2. Not Found Errors (NOT_FOUND)

HTTP Status Code: 404

Error CodeDescriptionExample Scenario
[MODULE]_NOT_FOUNDResource does not existQueried ID does not exist
[MODULE]_ROUTE_NOT_FOUNDRoute does not existAccessing undefined endpoint

Examples:

typescript
ITEM_NOT_FOUND   // Item ID does not exist
USER_NOT_FOUND   // User ID does not exist

3. Permission Errors (FORBIDDEN / UNAUTHORIZED)

HTTP Status Code: 401 (unauthenticated), 403 (no permission)

Error CodeDescriptionExample Scenario
AUTH_UNAUTHORIZEDNot logged in or invalid tokenJWT expired
[MODULE]_FORBIDDENNo permission to accessAttempting to access others' data

Examples:

typescript
AUTH_UNAUTHORIZED     // Token expired or missing
ITEM_FORBIDDEN        // Attempting to delete others' Item

4. Conflict Errors (CONFLICT)

HTTP Status Code: 409

Error CodeDescriptionExample Scenario
[MODULE]_CONFLICT_DUPLICATEResource conflictCreating existing resource
[MODULE]_CONFLICT_STATEState conflictOperation doesn't match current state

Examples:

typescript
USER_CONFLICT_DUPLICATE   // Email already registered
ITEM_CONFLICT_STATE       // Completed project cannot be deleted

5. Server Errors (INTERNAL_ERROR)

HTTP Status Code: 500

Error CodeDescriptionExample Scenario
INTERNAL_ERRORUnknown internal errorDatabase connection failed
DATABASE_ERRORDatabase errorPrisma query failed
EXTERNAL_SERVICE_ERRORExternal service errorThird-party API failed

Examples:

typescript
INTERNAL_ERROR             // Generic server error
DATABASE_ERROR             // Database operation failed
EXTERNAL_SERVICE_ERROR     // Third-party API call failed

6. Rate Limit Errors (RATE_LIMIT)

HTTP Status Code: 429

Error CodeDescriptionExample Scenario
RATE_LIMIT_EXCEEDEDExceeded request frequency limitMore than 100 requests in 1 minute

7.3 Error Response Format

Standard response structure:

typescript
interface ErrorResponse {
  success: false;
  error: {
    code: string;           // Error code
    message: string;        // User-friendly error message
    details?: unknown;      // Detailed information (optional, development only)
    timestamp?: string;     // Timestamp (optional)
    path?: string;          // Request path (optional)
  };
}

Response examples:

Validation error:

json
{
  "success": false,
  "error": {
    "code": "ITEM_VALIDATION_REQUIRED",
    "message": "Missing required field: title",
    "details": {
      "field": "title",
      "constraint": "required"
    }
  }
}

Not found error:

json
{
  "success": false,
  "error": {
    "code": "ITEM_NOT_FOUND",
    "message": "Item with ID 123 not found"
  }
}

Server error:

json
{
  "success": false,
  "error": {
    "code": "DATABASE_ERROR",
    "message": "Database operation failed, please try again later"
  }
}

7.4 Error Class Definition

src/lib/errors.ts:

typescript
export class AppError extends Error {
  constructor(
    public statusCode: number,
    public code: string,
    public message: string,
    public details?: unknown
  ) {
    super(message);
    this.name = 'AppError';
  }
}

// Validation error
export class ValidationError extends AppError {
  constructor(code: string, message: string, details?: unknown) {
    super(400, code, message, details);
  }
}

// Not found error
export class NotFoundError extends AppError {
  constructor(code: string, message: string) {
    super(404, code, message);
  }
}

// Permission error
export class UnauthorizedError extends AppError {
  constructor(message: string = 'Unauthorized access') {
    super(401, 'AUTH_UNAUTHORIZED', message);
  }
}

export class ForbiddenError extends AppError {
  constructor(code: string, message: string) {
    super(403, code, message);
  }
}

// Conflict error
export class ConflictError extends AppError {
  constructor(code: string, message: string) {
    super(409, code, message);
  }
}

// Server error
export class InternalError extends AppError {
  constructor(message: string = 'Internal server error') {
    super(500, 'INTERNAL_ERROR', message);
  }
}

7.5 Error Code Constants

src/constants/error-codes.ts:

typescript
// Item module error codes
export const ITEM_ERRORS = {
  NOT_FOUND: 'ITEM_NOT_FOUND',
  VALIDATION_REQUIRED: 'ITEM_VALIDATION_REQUIRED',
  VALIDATION_INVALID_AMOUNT: 'ITEM_VALIDATION_INVALID_AMOUNT',
  FORBIDDEN: 'ITEM_FORBIDDEN',
} as const;

// User module error codes
export const USER_ERRORS = {
  NOT_FOUND: 'USER_NOT_FOUND',
  CONFLICT_DUPLICATE: 'USER_CONFLICT_DUPLICATE',
  VALIDATION_INVALID_EMAIL: 'USER_VALIDATION_INVALID_EMAIL',
} as const;

// Authentication error codes
export const AUTH_ERRORS = {
  UNAUTHORIZED: 'AUTH_UNAUTHORIZED',
  VALIDATION_INVALID_EMAIL: 'AUTH_VALIDATION_INVALID_EMAIL',
  VALIDATION_REQUIRED: 'AUTH_VALIDATION_REQUIRED',
} as const;

7.6 Usage Examples

Service layer:

typescript
import { NotFoundError, ValidationError } from '@/lib/errors';
import { ITEM_ERRORS } from '@/constants/error-codes';

export const itemService = {
  async findById(id: number) {
    const item = await prisma.item.findUnique({ where: { id } });

    if (!item) {
      throw new NotFoundError(
        ITEM_ERRORS.NOT_FOUND,
        `Item with ID ${id} not found`
      );
    }

    return item;
  },

  async create(data: CreateItemDto) {
    if (!data.title) {
      throw new ValidationError(
        ITEM_ERRORS.VALIDATION_REQUIRED,
        'Missing required field: title',
        { field: 'title' }
      );
    }

    if (data.amount < 0) {
      throw new ValidationError(
        ITEM_ERRORS.VALIDATION_INVALID_AMOUNT,
        'Amount cannot be negative',
        { field: 'amount', value: data.amount }
      );
    }

    return prisma.item.create({ data });
  },
};

7.7 Frontend Error Handling

Error code mapping:

typescript
// client/src/constants/error-messages.ts
export const ERROR_MESSAGES: Record<string, string> = {
  // Item errors
  ITEM_NOT_FOUND: 'Item does not exist',
  ITEM_VALIDATION_REQUIRED: 'Please fill in required fields',
  ITEM_VALIDATION_INVALID_AMOUNT: 'Amount must be greater than 0',

  // User errors
  USER_NOT_FOUND: 'User does not exist',
  USER_CONFLICT_DUPLICATE: 'This email is already registered',
  USER_VALIDATION_INVALID_EMAIL: 'Email format is incorrect',

  // Authentication errors
  AUTH_UNAUTHORIZED: 'Please log in first',

  // Generic errors
  INTERNAL_ERROR: 'Server error, please try again later',
  RATE_LIMIT_EXCEEDED: 'Too many requests, please try again later',

  // Default error
  DEFAULT: 'Operation failed, please try again later',
};

export function getErrorMessage(code: string): string {
  return ERROR_MESSAGES[code] || ERROR_MESSAGES.DEFAULT;
}

API client:

typescript
// client/src/api/client.ts
import axios, { AxiosError } from 'axios';
import { getErrorMessage } from '@/constants/error-messages';

const apiClient = axios.create({
  baseURL: process.env.EXPO_PUBLIC_API_URL,
});

// Response interceptor
apiClient.interceptors.response.use(
  (response) => response,
  (error: AxiosError<ErrorResponse>) => {
    const errorCode = error.response?.data?.error?.code || 'DEFAULT';
    const errorMessage = getErrorMessage(errorCode);

    // Return standardized error
    return Promise.reject({
      code: errorCode,
      message: errorMessage,
      originalError: error,
    });
  }
);

8. Prohibited Practices

The following practices are strictly prohibited in generated code:

  1. Using any type - Use unknown and add type guards
  2. Hardcoding sensitive information - Use environment variables
  3. Ignoring error handling - All async operations need try-catch
  4. Using console.log for debugging - Use structured logging
  5. Inline styles - Use StyleSheet
  6. Skipping type definitions - All public interfaces need types
  7. Using var - Use const or let
  8. Using == - Use ===
  9. Modifying function parameters - Create new objects/arrays
  10. Nesting more than 3 levels of if - Early return or split function

FAQ

1. Why is using any type prohibited?

Type Safety

The any type bypasses TypeScript's type checking, leading to runtime errors. Using unknown and adding type guards ensures type safety while maintaining flexibility.

Example comparison:

typescript
// ❌ Dangerous
function processData(data: any) {
  return data.value; // Runtime error if data has no value property
}

// ✅ Safe
function processData(data: unknown) {
  if (typeof data === 'object' && data !== null && 'value' in data) {
    return (data as { value: string }).value;
  }
  throw new Error('Invalid data format');
}

2. How do frontend and backend share error code definitions?

You can share them through:

  1. Type file sharing: Export error code types in the backend project, frontend fetches via API or manually syncs
  2. Monorepo: Frontend and backend in the same repository, can directly reference
  3. OpenAPI/Swagger: Define error codes in API documentation, frontend auto-generates types

3. How do error codes support internationalization?

Recommended approach:

typescript
// Backend: return error code and parameters
{
  "success": false,
  "error": {
    "code": "ITEM_VALIDATION_REQUIRED",
    "message": "Missing required field",
    "details": { "field": "title" }
  }
}

// Frontend: map error code to localized messages
export const ERROR_MESSAGES = {
  en: { ITEM_VALIDATION_REQUIRED: 'Missing required field' },
  zh: { ITEM_VALIDATION_REQUIRED: '缺少必填字段' },
};
  1. Check backend logs: Confirm error code is thrown correctly
  2. Check frontend mapping: Confirm there's a corresponding entry in ERROR_MESSAGES
  3. Use network panel: View the complete error response returned by API
  4. Show details in development: Development environment returns detailed error information

Lesson Summary

In this lesson, we covered in detail the standards that AI App Factory generated code must follow:

  • General standards: Language formatting, naming conventions, file organization
  • TypeScript standards: Type definitions, type assertions, generics
  • Backend standards: Express routes, controllers, service layer, error handling
  • Frontend standards: Component structure, hook standards, style standards, navigation
  • Prisma standards: Schema definition, query standards
  • Comment standards: When to add comments, comment format
  • Error code standards: Error code structure, standard error types, error response format
  • Prohibited practices: 10 strictly prohibited practices

Following these standards ensures consistent code quality, maintainability, and readability.


Next Up

In the next lesson, we learn Tech Stack Details.

You will learn:

  • The tech stack used by generated applications
  • The combination of Node.js + Express + Prisma + React Native
  • Features and best practices of each technology

Appendix: Source Code Reference

Click to expand source code locations

Last updated: 2026-01-29

FeatureFile PathLine Numbers
Code standards documentsource/hyz1992/agent-app-factory/policies/code-standards.md1-604
Error code standardssource/hyz1992/agent-app-factory/policies/error-codes.md1-469

Key Standards:

  • Code standards: General standards, TypeScript standards, backend standards, frontend standards, Prisma standards, comment standards, ESLint configuration, prohibited practices
  • Error code standards: Error code structure, standard error types (VALIDATION, NOT_FOUND, FORBIDDEN, CONFLICT, INTERNAL_ERROR, RATE_LIMIT), error response format, frontend/backend error handling

Key Configuration:

  • Backend ESLint: @typescript-eslint/no-explicit-any rule set to error
  • Frontend ESLint: @typescript-eslint/no-explicit-any rule set to error
  • Error code format: [MODULE]_[ERROR_TYPE]_[SPECIFIC]
  • Error response structure: Contains success, error.code, error.message, error.details