Files
La-Fabrik/.agent/skills/best-practices.md
T
2026-04-15 11:06:41 +02:00

7.1 KiB

Skill — Best Practices

Principles

Generate code that is simple, understandable, reviewable, scalable, optimized, and modern. Follow W3C web standards and platform conventions.

Naming Conventions

Files

Type Convention Example
Components PascalCase WorkshopZone.tsx
Hooks camelCase with use prefix useGameState.ts
Managers PascalCase GameManager.ts
Utils PascalCase Dispose.ts
Data PascalCase missions.ts
Shaders kebab-case hologram.vert.glsl

Variables & Functions

Type Convention Example
Variables camelCase activeZone, missionStep
Functions camelCase startMission(), setActiveZone()
Constants UPPER_SNAKE_CASE MAX_SPEED, DEFAULT_PHASE
React components PascalCase function WorkshopZone()
React hooks camelCase with use useGameState(), useFrame()
Classes PascalCase class GameManager
Interfaces/Types PascalCase type GameSnapshot, interface ZoneData
Enums PascalCase enum Phase (prefer string literals instead)

CSS Classes (if applicable)

Use kebab-case:

.mission-hud {
}
.narrative-overlay {
}

File Structure

Every file starts with a route comment:

# route path src/world/zones/WorkshopZone.tsx

Code Style

Simplicity First

// Good — clear, direct
function getZoneRadius(zone: Zone): number {
  return zone.radius;
}

// Bad — over-abstracted
function getZoneRadius(zone: Zone): number {
  return zone[ZoneFields.RADIUS] ?? RADIUS_DEFAULTS[zone.type]?.default ?? 50;
}

Early Return

// Good
if (!gltf) return null;
if (!visible) return null;

return <primitive object={gltf.scene} />;

// Bad
if (gltf && visible) {
  return <primitive object={gltf.scene} />;
}
return null;

Destructuring

// Good
const { position, rotation, scale } = object;

// Good — explicit
const game = GameManager.getInstance();
const { phase, activeZone } = game.getState();

Avoid Nested Callbacks

// Good — flat structure
useEffect(() => {
  const mixer = new THREE.AnimationMixer(model);

  return () => mixer.stopAllAction();
}, [model]);

// Bad — nested
useEffect(() => {
  useEffect(() => {
    const mixer = new THREE.AnimationMixer(model);
  }, [model]);
}, []);

TypeScript Rules

Explicit Types for Exports

// Good — explicit return type
export function useGameState(): GameSnapshot {
  // ...
}

// Good — explicit parameter types
export function setPhase(phase: Phase): void {
  // ...
}

Interface over Type (for object shapes)

// Good
interface ZoneData {
  id: string;
  position: [number, number, number];
  radius: number;
}

// OK — simple unions
type Phase = "loading" | "exploring" | "cinematic";

Never use any

// Good
const ref = useRef<THREE.Group>(null);

// Bad
const ref = useRef<any>(null);

React Patterns

Component Structure

# route path src/world/zones/WorkshopZone.tsx

import { useRef, useEffect } from "react";
import * as THREE from "three";
import { useFrame } from "@react-three/fiber";
import { useGLTF } from "@react-three/drei";

export function WorkshopZone() {
  const ref = useRef<THREE.Group>(null);
  const gltf = useGLTF("/models/workshop/ebike.glb");

  useFrame((_, delta) => {
    // per-frame logic
  });

  useEffect(() => {
    // setup
    return () => {
      // cleanup
    };
  }, []);

  return <primitive ref={ref} object={gltf.scene.clone()} />;
}

Hooks Return Simple Values

// Good — returns simple object
export function useGameState() {
  const game = GameManager.getInstance();
  const [state, setState] = useState(game.getState());

  useEffect(() => {
    return game.subscribe(() => setState({ ...game.getState() }));
  }, [game]);

  return state;
}

// Bad — returns methods that modify state directly
export function useGameActions() {
  const game = GameManager.getInstance();
  return {
    setPhase: (p: Phase) => game.setPhase(p),
    startMission: (id: string) => game.startMission(id),
  };
}

Performance

useRef for Mutable Values

// Good — no re-render
const position = useRef(new THREE.Vector3());

// Bad — triggers re-render every frame
const [position, setPosition] = useState(new THREE.Vector3());

Memoize Expensive Computations

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

const memoizedCallback = useCallback(
  (x: number) => doSomething(x),
  [dependency],
);

Lazy Loading

// Components
const HeavyComponent = lazy(() => import("./HeavyComponent"));

// Assets
useGLTF.preload("/models/map/base.glb");

Scalability

Single Responsibility

// Good — focused component
export function WorkshopZone() {
  // Only handles workshop zone logic
}

// Bad — does everything
export function WorkshopZone() {
  // Handles zone logic + audio + cinematics + missions
}

Separate Data from View

// Good — data in src/data/
// src/data/missions.ts
export const missions = {
  workshop: { steps: [...] },
};

// Bad — data inline
export function WorkshopZone() {
  const missions = [{ steps: [...] }]; // scattered everywhere
}

Constants for Magic Numbers

// Good
const DEFAULT_CAMERA_DISTANCE = 5;
const ZONE_DETECTION_RADIUS = 20;

// Bad
mesh.position.set(5, 0, 0); // magic number

Accessibility (W3C)

Semantic HTML

// Good
<button onClick={handleInteract} aria-label="Interact with bike">
  <Model />
</button>

// Bad
<div onClick={handleInteract}>
  <Model />
</div>

Keyboard Navigation

<button
  onClick={openDialogue}
  onKeyDown={(e) => e.key === "Enter" && openDialogue()}
>
  Talk
</button>

Rules

  1. Simplicity — Every line of code must be justified. If it's not obviously necessary, remove it.
  2. Readability — Code is read 10x more than it's written. Optimize for the reader.
  3. Reviewability — Pull requests should be understandable in < 5 minutes.
  4. Scalability — Architecture should support growth without refactoring existing code.
  5. Performance — Don't optimize prematurely, but don't introduce obvious bottlenecks.
  6. Modern — Use ES2022+ features, TypeScript strict mode, React hooks over class lifecycle.
  7. W3C — Follow web standards: semantic HTML, ARIA, keyboard navigation.
  8. No Over-Engineering — Avoid patterns that add complexity without benefit (factories for 1 instance, generic wrappers for single use cases, etc.)