Files
youtube-studio-flow/documentation/YouTube Studio Flow/04 - Design Guidelines/05 - Code Conventions.md
T

5.8 KiB

05 - Code Conventions

Language and style conventions for all TypeScript/TSX code in YouTube Studio Flow. These apply to both the frontend and backend unless noted otherwise. For CSS-specific rules, see 02 - CSS Conventions.


Comments

Default to writing no comments.

Only add a comment when the why is non-obvious: a hidden constraint, a subtle invariant, a browser quirk, a workaround for a specific external bug, or behavior that would surprise a competent reader encountering it for the first time. If removing the comment wouldn't cause confusion, don't write it.

Never write:

  • Comments that describe what the code does (the code already does that)
  • Multi-paragraph docstrings on functions or classes
  • Multi-line comment blocks
  • Cross-reference notes ("added for issue #123", "used by the sync feature")
  • Section dividers (// --- helpers ---)

Acceptable:

// BullMQ silently drops jobs if Redis evicts keys — must use noeviction policy
const client = new Redis({ maxmemoryPolicy: 'noeviction' });

// Single-pass replacement avoids re-substituting inside already-replaced values
const result = template.replace(pattern, (match) => tokens[match] ?? match);

No premature abstraction

Add abstractions exactly when they are needed, not before. Three similar lines of code is better than a helper function introduced speculatively for hypothetical future use.

If a pattern appears twice, note it. If it appears three times with meaningful variation, consider abstracting. If the abstraction would be more complex than the repetition, don't.


Error handling scope

Only validate and handle errors at system boundaries:

  • User input (request bodies, form submissions)
  • External API calls (YouTube API, Google OAuth)
  • Queue job payloads at the processor entry point

Trust internal code. Trust Prisma's type guarantees. Do not add defensive try/catch around internal service calls for errors that cannot happen under normal operation.


No backwards-compat shims

When changing or removing behavior, change or remove it. Do not leave:

  • Unused variables prefixed with _ to signal "formerly used"
  • Re-exports of deleted types for "compatibility"
  • // removed comments where code used to be
  • Feature flags gating old vs. new behavior

TypeScript

  • Use strict TypeScript throughout. "strict": true is set in both tsconfig.json files.
  • Avoid any. The only acceptable uses are at Prisma enum boundaries where the type system cannot express a legitimate constraint, and when interfacing with genuinely untyped external data (e.g. raw OAuth token payloads).
  • Use as any sparingly. If you reach for it, consider whether a type assertion (as SpecificType) or a type guard is more appropriate.
  • Prefer explicit return types on exported functions and service methods. Inference is acceptable for small private helpers.
  • Use type for object shapes and union types. Use interface for contracts intended to be extended or implemented.
// Object shape — use type
type VideoFilters = {
  search?: string;
  status?: PrivacyStatus;
  page: number;
};

// Extendable contract — use interface
interface RenderInput {
  videoId: string;
  blockOrder: string[];
  blockOverrides: Record<string, BlockOverride>;
}

Naming conventions

Context Convention Examples
React components PascalCase VideoTable, BlockEditor
CSS module classes camelCase styles.clickableRow, styles.headerCell
TypeScript interfaces and types PascalCase VideoFilters, RenderInput
Frontend API functions camelCase verb + noun fetchVideos, updateVideo, createBlock
Backend service methods camelCase verb + noun findAll, findOne, create, update, remove
Queue job name strings kebab-case 'lint', 'youtube-sync', 'bulk-metadata'
TanStack Query keys array of strings ['videos'], ['video', id], ['blocks']
Zustand store files camelCase with use prefix useAuthStore.ts, useUIStore.ts

Import order

Organize imports in this order within any TypeScript or TSX file. An empty line between each group:

  1. React imports
  2. Next.js imports (next/navigation, next/image, etc.)
  3. Third-party libraries (@tanstack/react-query, lucide-react, etc.)
  4. Internal aliases (@/components/..., @/lib/..., @/store/...)
  5. Relative imports (../utils, ./helpers)
  6. Style imports (CSS modules, always last)
import { useState, useRef, forwardRef } from 'react';

import { useRouter } from 'next/navigation';

import { useQuery, useMutation } from '@tanstack/react-query';
import { Save, X } from 'lucide-react';

import Modal from '@/components/shared/Modal';
import { updateVideo } from '@/lib/api';
import { useAuthStore } from '@/store/useAuthStore';

import { formatDate } from '../utils/date';

import styles from './VideoEditor.module.css';
import f from '@/components/shared/FormField.module.css';

No dead code

Remove unused imports as you work. Remove unused variables. Remove unreachable branches. Delete unused files rather than leaving them in place.

Do not leave console.log statements in committed code. Use the logger (nestjs/common Logger on the backend) for intentional output.


Validation

Backend: Use class-validator decorators on DTO classes. Every request body that reaches a controller must go through a validated DTO.

export class CreateBlockDto {
  @IsString()
  @IsNotEmpty()
  name: string;

  @IsEnum(BlockType)
  type: BlockType;

  @IsString()
  @IsOptional()
  content?: string;
}

Frontend: Validate at the form submission boundary only. Inside a component, trust the types from @/lib/api.ts. Do not add runtime type checks on data returned by the API.