
Structural Integrity
- 1 installs
- 2 repo stars
- Updated March 18, 2026
- masanao-ohba/claude-manifests
Enforces structural integrity in React/TypeScript code: single source of truth, minimal branching, responsibility separation, and derived state patterns.
About
A review checklist and implementation guide enforcing single source of truth, minimal branching, and derived state in React/TypeScript code. A developer uses it when writing or reviewing changes to keep control flow justified.
- Every branch must map to a functional requirement
- Emphasizes derived state and responsibility separation
Structural Integrity by the numbers
- 1 all-time installs (skills.sh)
- Ranked #984 of 1,352 Code Review & Quality skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/masanao-ohba/claude-manifests --skill structural-integrityAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 2 |
| Last updated | March 18, 2026 |
| Repository | masanao-ohba/claude-manifests ↗ |
What it does
Enforces structural integrity in React/TypeScript code: single source of truth, minimal branching, responsibility separation, and derived state patterns.
Files
Structural Integrity Standards
Core Philosophy
Every conditional branch, loop, and exception handler must justify its existence by mapping to a specific functional requirement. Over-abstraction that obscures business logic is as harmful as duplication.
---
1. Single Source of Truth
Types
- Define each type/interface in one canonical file — all consumers import from that file
- Use
Pick<T, K>orOmit<T, K>to create partial views instead of declaring local re-definitions - Never create
FooLikeor local interface copies of an existing type
// BAD: local re-declaration
interface NewMessageManagerLike {
getUnreadCount(group: Message[]): number;
}
// GOOD: derive from canonical type
import { NewMessageManager } from '@/lib/types/newMessageManager';
type Props = { manager: Pick<NewMessageManager, 'getUnreadCount'> };Constants
- All behavioral values (thresholds, delays, margins, sizes) must be named constants in a centralized constants file
- Each constant includes a specification comment explaining what it controls
- No magic numbers in component or hook files
// BAD
{ rootMargin: '200px' }
setTimeout(fn, 750);
// GOOD
import { VIEWPORT_PRERENDER_MARGIN_PX, NOTIFICATION_AUTOREAD_FADE_MS } from '@/lib/constants/ui';
{ rootMargin: `${VIEWPORT_PRERENDER_MARGIN_PX}px` }
setTimeout(fn, NOTIFICATION_AUTOREAD_FADE_MS);Shared Logic
- When two or more files compute the same value, extract to a shared utility
- Utilities must be pure functions — no side effects, no store access
- Place in
lib/utils/with a descriptive filename matching the function's domain
---
2. Minimal Branching and Loops
Conditional Branches
- Every
if, ternary, orswitchmust map to a distinct functional requirement - Reduce branching by using data-driven approaches (maps, lookups, array methods)
- Avoid nested conditionals — use early returns or guard clauses
- Ternaries count as conditionals — don't chain or nest them
Loops
- Avoid redundant iterations — combine related operations into a single pass
- Use
Set.has()(O(1)) instead ofArray.indexOf()/Array.find()(O(n)) for membership checks - When iterating collections, prefer declarative methods (
map,filter,reduce) over imperativeforloops
Exception Handlers
- Every
catchblock must either log the error or propagate it — never silently swallow - After handling a cancellation/expected error,
returnimmediately — don't fall through to error rendering - Match catch scope to the operation: don't wrap unrelated code in the same try block
---
3. Responsibility Separation
Component Responsibilities
- Presentational components receive all data and actions via props — no direct store access
- Container components / hooks own data fetching and state management
- A component that grows beyond ~200 lines likely has multiple responsibilities — extract hooks or sub-components
// BAD: Presentational component accessing store directly
function NotificationItem({ notification }) {
const { markAsRead } = useNotificationStore(); // ← store leak
...
}
// GOOD: Actions passed as props
function NotificationItem({ notification, onMarkAsRead, onDelete }) {
...
}Hook Responsibilities
- Each custom hook has one job — don't combine pagination logic with project tab computation
- Return a minimal, typed interface — not the entire internal state
- When a hook's return value is consumed by React, use
useState(not refs + forceUpdate) so React's render cycle detects changes naturally
File Responsibilities
- Each file exports one primary concern
- Co-locate types with their primary consumer unless shared by 3+ files (then promote to
lib/types/) - Co-locate constants with their domain unless shared across domains (then promote to
lib/constants/)
---
4. Derived State
Selector Pattern
- State that can be computed from other state must be a selector, not a manually maintained field
- When store state changes, derived values update automatically through selectors — no manual synchronization across mutation paths
// BAD: Manual maintenance in N mutation paths
markAsRead(id) {
set({ notifications: updated, unreadCount: updated.filter(n => !n.read).length });
}
markAllAsRead() {
set({ notifications: updated, unreadCount: 0 });
}
// GOOD: Single selector, zero maintenance
export const selectUnreadCount = (state) =>
state.notifications.filter((n) => !n.read).length;Version Counter for Ref-Based Hooks
- When a hook stores mutable data in refs (for performance), use a
useStateversion counter to trigger React re-renders - Increment the counter after each mutation
- Never use
forceUpdate({})— it breaks React's mental model and is invisible to parent components
---
5. CSS vs JavaScript
- Visual concerns (transitions, opacity, colors, scrollbar styling) belong in CSS — not
classList.add()or inline style manipulation - Use CSS classes toggled by React state, not direct DOM manipulation
- Consolidate related styles in a single CSS file — avoid duplicate inline
<style>blocks
// BAD: DOM manipulation for visual effect
el.classList.add('auto-reading');
// GOOD: React state drives CSS class
<div className={cn('notification-item', isAutoReading && 'auto-reading')} />---
6. Resource Management
Singletons for Shared Resources
- Browser APIs that are expensive to instantiate (Worker, IntersectionObserver) should be module-level singletons with a fixed upper bound
- Use
WeakMap<Element, callback>for callback routing to allow element GC - Never create per-component instances of expensive resources
Cleanup
- Every
observe()must have a correspondingunobserve()ordisconnect()in cleanup - Every
setTimeout/setIntervalmust be cleared in the effect cleanup - Ref Maps that track DOM elements must evict stale entries when the data source shrinks
Promise Lifecycle
cancel()must reject the promise — deleting a pending entry without rejection leaks the promise and its closures- Track which resource owns which pending request to avoid cascading failures
---
Anti-Patterns Summary
| Anti-Pattern | Fix |
|---|---|
| Local type re-declarations | Import from canonical source, use Pick<> |
| Magic numbers | Named constants with spec comments |
| Store access in presentational components | Pass actions as props |
| Manual derived state across N paths | Selector function |
forceUpdate({}) / useReducer hack | Version counter useState |
classList.add() for visual effects | CSS class via React state |
| Per-component Worker/Observer | Module-level singleton pool |
| Silent catch blocks | Log or propagate, then return |
| Nested ternaries / chained conditionals | Early returns, guard clauses, data-driven |
| Over-abstraction hiding business logic | Direct implementation with clear responsibility |