From claude-skills
A concise, opinionated reference of C# / Unity / general engineering principles to follow when writing or reviewing code (readonly defaults, no-comment-if-code-explains, no LINQ in hot paths, server-authoritative networking, scale-aware design, anti-patterns to refuse). Use when the user asks about coding standards, style guide, best practices, how to write idiomatic C#, or "what rules should I follow here". Loaded automatically by the `coder` agent.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-skills:coding-principlesThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
A tight, opinionated standard. The agent (and humans) should apply these without being asked. If a rule conflicts with the user's explicit instruction, the user wins.
A tight, opinionated standard. The agent (and humans) should apply these without being asked. If a rule conflicts with the user's explicit instruction, the user wins.
readonly by default. Private fields not reassigned post-ctor → readonly. Literal constants → const. Modifier order: static readonly, never the reverse. For value carriers, prefer record or init-only props.var only when the RHS makes the type obvious. var users = new List<Player>(); yes. var x = GetThing(); no — write the type.state switch { Dealing => ..., Playing => ..., _ => ... }. Use is { Health: > 0 } p over null+property checks. ?? / ??= for null fallbacks.Where/Select/ToList inside Update/FixedUpdate/LateUpdate — allocations stress GC. Outside hot paths, use LINQ idiomatically:
dict.Values when keys are unused — don't iterate KVPs and remap..Any() over .Count() > 0.list.Count (property) over list.Count() (extension).foreach (var kv in ...) { var v = kv.Value; }.IReadOnlyList<T> / IReadOnlyCollection<T>. Never the backing List<T>. Mutations go through methods you control.async discipline. Awaiting methods return Task / Task<T> and end in Async. async void only for event handlers — anywhere else it eats exceptions. Never .Result / .Wait() on a Task. Library / non-UI: ConfigureAwait(false).using var declaration form over the older block form unless scoping demands it. Every IDisposable is disposed on every path._camelCase private fields, PascalCase public members/types/consts, camelCase locals/params, IPascal interfaces, TName generic params, XxxAsync for awaitables.// increment i is noise. // Mirror serializes SyncList deltas, so we batch mutations here to avoid N RPCs is signal. Default to writing no comments.const, enum, or ScriptableObject. if (players.Count >= 4) → if (players.Count >= MaxPlayers).[SerializeField] private over public for inspector-edited fields. Encapsulation preserved; inspector still sees them.GetComponent, Camera.main, transform, Find in Awake / OnEnable. Calling them per-frame is a documented hotspot.Update-tier methods: string + / interpolation, boxing (int → object), new instantiations, capturing lambdas (() => _x allocates a closure), params arrays.FixedUpdate for physics, Update for input/visuals, events for everything else. Polling state that an event could fire is the most common LLM mistake.UniTask (not bare Task) for async/await in Unity. Bare Task doesn't honor Unity's main thread or PlayerLoop and won't cancel on scene unload.[Command], is validated server-side, and the result is broadcast via [ClientRpc] / SyncVar / SyncList. Never trust client-reported state.syncInterval for non-critical state; use [SyncVar(hook=...)] for change-driven UI rather than polling.SyncList mutations are per-op deltas. Replacing every element each turn floods the network. Mutate in place or Clear() + AddRange deliberately.TargetRpc, not ClientRpc).lock only on shared mutable state — and lock on private readonly object _gate = new(), never this, never a type.Interlocked for counters; ConcurrentDictionary over lock + Dictionary. Don't roll your own.CancellationToken flows through every async method. Long-running async work without one is a bug..Result / .Wait() on Tasks in code that runs on a SynchronizationContext (Unity main thread, UI) — instant deadlock. Async all the way down.MaxPlayers from GameConfig (SO), not const 4. Loops and UI derive from it.players.Any(p => other.Any(...)), stop — at 4 it's fine, at 8+ rewrite with a dict / hashset.IFooFactoryFactory for two cases. Do put player count behind a const / config so 4 → 8 is a one-line change, not a rewrite.GameManager. Split by responsibility: TurnController, DealService, ScoreTracker. Singletons are last resort, not default.catch (Exception) { } or catch { Debug.Log(e); } that swallow. Catch specific types; rethrow or fail loudly.const, enum, or SO.List<T> getters. Use IReadOnlyList<T> + intent-named mutators.bool _isReady checked every frame should be an event Action Ready.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub paologum/claude-skills --plugin claude-skills