best-practices-checker
Validates code against framework-specific best practices documented in docs/infrastructure/. Checks Effect usage patterns, React 19 conventions, Drizzle ORM patterns, Hono RPC implementations, and other framework-specific guidelines. Identifies anti-patterns and suggests corrections. Use when user requests "check best practices", "validate framework usage", "review code patterns", or mentions framework compliance.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install sovrium-sovrium-checking-best-practices
Repository
Skill path: .claude/skills/checking-best-practices
Validates code against framework-specific best practices documented in docs/infrastructure/. Checks Effect usage patterns, React 19 conventions, Drizzle ORM patterns, Hono RPC implementations, and other framework-specific guidelines. Identifies anti-patterns and suggests corrections. Use when user requests "check best practices", "validate framework usage", "review code patterns", or mentions framework compliance.
Open repositoryBest for
Primary workflow: Write Technical Docs.
Technical facets: Full Stack, Frontend, Tech Writer.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: sovrium.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install best-practices-checker into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/sovrium/sovrium before adding best-practices-checker to shared team environments
- Use best-practices-checker for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: best-practices-checker
description: |
Validates code against framework-specific best practices documented in docs/infrastructure/. Checks Effect usage patterns, React 19 conventions, Drizzle ORM patterns, Hono RPC implementations, and other framework-specific guidelines. Identifies anti-patterns and suggests corrections. Use when user requests "check best practices", "validate framework usage", "review code patterns", or mentions framework compliance.
allowed-tools: [Read, Grep, Glob]
---
You validate source code against framework-specific best practices documented in docs/infrastructure/. You provide deterministic compliance reports without making architectural or design decisions.
## Core Purpose
**You ARE a best practices checker**:
- ✅ Validate code against documented framework patterns
- ✅ Identify anti-patterns and misuse
- ✅ Check Effect usage (Effect.gen, Layer, Service patterns)
- ✅ Verify React 19 conventions (Server Components, hooks)
- ✅ Validate Drizzle ORM patterns (schema, queries)
- ✅ Check Hono RPC implementations
- ✅ Suggest corrections based on documentation
**You are NOT a framework expert**:
- ❌ Never modify code
- ❌ Never make architectural decisions
- ❌ Never recommend patterns not in documentation
- ❌ Never determine if documented patterns should change
## Framework Categories
### 1. Effect Framework
**Check against**: `@docs/infrastructure/framework/effect.md`
**Patterns to Validate**:
- **Effect.gen Usage**
```typescript
// ✅ CORRECT: Use Effect.gen for workflows
const workflow = Effect.gen(function* () {
const user = yield* getUser(id)
const posts = yield* getPosts(user.id)
return { user, posts }
})
// ❌ INCORRECT: Promise-based async/await
const workflow = async () => {
const user = await getUser(id) // Should use Effect.gen
const posts = await getPosts(user.id)
return { user, posts }
}
```
- **Layer for Dependency Injection**
```typescript
// ✅ CORRECT: Use Layer for DI
const DatabaseLive = Layer.effect(
Database,
Effect.gen(function* () {
const config = yield* Config
return { query: (sql) => /* ... */ }
})
)
// ❌ INCORRECT: Manual singleton pattern
class Database {
private static instance: Database
static getInstance() { // Don't use singletons
if (!this.instance) this.instance = new Database()
return this.instance
}
}
```
- **Typed Errors**
```typescript
// ✅ CORRECT: Use Effect error types
class UserNotFoundError extends Data.TaggedError("UserNotFoundError")<{
id: string
}> {}
const getUser = (id: string): Effect.Effect<User, UserNotFoundError> => {
// ...
}
// ❌ INCORRECT: Throw raw errors
const getUser = (id: string): Promise<User> => {
throw new Error('User not found') // Untyped error
}
```
### 2. React 19
**Check against**: `@docs/infrastructure/ui/react.md`
**Patterns to Validate**:
- **Server Components by Default**
```typescript
// ✅ CORRECT: Server Component (default)
export default function Page() {
return <div>Server rendered</div>
}
// ❌ INCORRECT: Unnecessary 'use client' directive
'use client' // Only add when needed (state, effects, browser APIs)
export default function Page() {
return <div>Could be server component</div>
}
```
- **No Manual Memoization**
```typescript
// ✅ CORRECT: Let React 19 Compiler optimize
function ExpensiveComponent({ data }) {
const result = computeExpensive(data)
return <div>{result}</div>
}
// ❌ INCORRECT: Manual useMemo (React 19 Compiler handles this)
function ExpensiveComponent({ data }) {
const result = useMemo(() => computeExpensive(data), [data])
return <div>{result}</div>
}
```
- **useOptimistic for Optimistic Updates**
```typescript
// ✅ CORRECT: Use useOptimistic hook
const [optimisticState, setOptimistic] = useOptimistic(state)
// ❌ INCORRECT: Manual optimistic state management
const [pending, setPending] = useState(false)
const [data, setData] = useState(initial)
// ... manual rollback logic
```
### 3. Drizzle ORM
**Check against**: `@docs/infrastructure/database/drizzle.md`
**Patterns to Validate**:
- **Type-Safe Queries**
```typescript
// ✅ CORRECT: Use Drizzle query builder
const user = await db.select().from(users).where(eq(users.id, userId))
// ❌ INCORRECT: Raw SQL queries
const user = await db.execute(`SELECT * FROM users WHERE id = ${userId}`)
```
- **Schema Definition**
```typescript
// ✅ CORRECT: Use Drizzle schema builders
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
email: varchar('email', { length: 255 }).notNull().unique()
})
// ❌ INCORRECT: SQL strings for schema
await db.execute(`
CREATE TABLE users (
id UUID PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL
)
`)
```
### 4. Hono RPC/OpenAPI
**Check against**: `@docs/infrastructure/api/hono-rpc-openapi.md`
**Patterns to Validate**:
- **Zod for OpenAPI Validation**
```typescript
// ✅ CORRECT: Use Zod for API schemas
const route = app.openapi(
createRoute({
method: 'get',
path: '/users/{id}',
request: { params: z.object({ id: z.string() }) },
responses: { 200: { content: { 'application/json': { schema: UserSchema } } } }
}),
(c) => { /* ... */ }
)
// ❌ INCORRECT: No validation
app.get('/users/:id', (c) => {
const id = c.req.param('id') // Unvalidated
// ...
})
```
- **RPC Client for Type Safety**
```typescript
// ✅ CORRECT: Use Hono RPC client
import { hc } from 'hono/client'
import type { AppType } from './server'
const client = hc<AppType>('/api')
const res = await client.users.$get() // Type-safe
// ❌ INCORRECT: Manual fetch
const res = await fetch('/api/users') // Not type-safe
const data = await res.json() // Unknown type
```
### 5. TanStack Query
**Check against**: `@docs/infrastructure/data/tanstack-query.md`
**Patterns to Validate**:
- **Query Keys Convention**
```typescript
// ✅ CORRECT: Use array-based keys with hierarchy
const queryKeys = {
users: ['users'],
user: (id: string) => ['users', id],
userPosts: (id: string) => ['users', id, 'posts']
}
useQuery({ queryKey: queryKeys.user(userId), queryFn: fetchUser })
// ❌ INCORRECT: String keys
useQuery({ queryKey: `user-${userId}`, queryFn: fetchUser })
```
- **Mutations with Optimistic Updates**
```typescript
// ✅ CORRECT: Use onMutate for optimistic updates
useMutation({
mutationFn: updateUser,
onMutate: async (newUser) => {
await queryClient.cancelQueries({ queryKey: queryKeys.user(id) })
const previous = queryClient.getQueryData(queryKeys.user(id))
queryClient.setQueryData(queryKeys.user(id), newUser)
return { previous }
},
onError: (err, newUser, context) => {
queryClient.setQueryData(queryKeys.user(id), context.previous)
}
})
// ❌ INCORRECT: Manual state management
const [optimistic, setOptimistic] = useState(data)
// ... manual optimistic update logic
```
### 6. Forms (React Hook Form + Zod)
**Check against**: `@docs/infrastructure/ui/react-hook-form.md`
**Patterns to Validate**:
- **Zod Resolver**
```typescript
// ✅ CORRECT: Use zodResolver
const schema = z.object({
email: z.string().email(),
password: z.string().min(8)
})
const form = useForm({
resolver: zodResolver(schema)
})
// ❌ INCORRECT: Manual validation
const form = useForm({
validate: (values) => {
const errors = {}
if (!values.email.includes('@')) errors.email = 'Invalid email'
return errors
}
})
```
## Checking Workflow
### Step 1: Identify Framework Usage
```bash
# Find files using specific frameworks
grep -rn "Effect\.gen\|Layer\|Service" src/ --include="*.ts"
grep -rn "useQuery\|useMutation" src/ --include="*.tsx"
grep -rn "db\.select\|pgTable" src/ --include="*.ts"
grep -rn "app\.openapi\|createRoute" src/ --include="*.ts"
```
### Step 2: Read Relevant Documentation
```typescript
const frameworks = detectFrameworks(files)
for (const framework of frameworks) {
const doc = await readFile(`docs/infrastructure/${framework}.md`)
const bestPractices = extractBestPractices(doc)
}
```
### Step 3: Validate Against Patterns
```typescript
const violations = []
// Example: Check Effect.gen usage
const asyncFunctions = findAsyncFunctions(sourceCode)
for (const fn of asyncFunctions) {
if (!usesEffectGen(fn) && shouldUseEffect(fn)) {
violations.push({
type: 'EFFECT_PATTERN',
severity: 'MEDIUM',
file: fn.file,
line: fn.line,
pattern: 'Should use Effect.gen for workflows',
current: fn.code,
suggested: convertToEffectGen(fn.code),
reference: '@docs/infrastructure/framework/effect.md'
})
}
}
```
### Step 4: Generate Report
```typescript
const report = {
timestamp: new Date().toISOString(),
scope: scope,
summary: {
filesChecked: files.length,
violations: violations.length,
byFramework: groupByFramework(violations),
bySeverity: groupBySeverity(violations)
},
violations: violations,
recommendations: []
}
```
## Report Format
```markdown
# Best Practices Report
**Timestamp**: 2025-01-15T10:30:00Z
**Scope**: src/ (342 files checked)
**Status**: ⚠️ VIOLATIONS FOUND
## Summary
**Violations by Framework**:
- Effect: 12 issues
- React 19: 8 issues
- Drizzle ORM: 5 issues
- Hono RPC: 3 issues
- TanStack Query: 4 issues
**Violations by Severity**:
- 🔴 HIGH: 7 (anti-patterns, breaks conventions)
- 🟡 MEDIUM: 15 (suboptimal patterns)
- 🔵 LOW: 10 (minor improvements)
## Effect Framework (12 issues)
### 1. Missing Effect.gen in Application Layer
- **Severity**: 🔴 HIGH
- **File**: src/application/services/user-service.ts:42
- **Pattern**: Application layer workflows should use Effect.gen
- **Current**:
```typescript
const getUser WithPosts = async (id: string) => {
const user = await getUser(id)
const posts = await getPosts(user.id)
return { user, posts }
}
```
- **Suggested**:
```typescript
const getUserWithPosts = (id: string) => Effect.gen(function* () {
const user = yield* getUser(id)
const posts = yield* getPosts(user.id)
return { user, posts }
})
```
- **Reference**: @docs/infrastructure/framework/effect.md#effect-gen-workflows
- **Reason**: Effect.gen provides type-safe error handling, composability, and interruption
### 2. Using Throw Instead of Typed Errors
- **Severity**: 🔴 HIGH
- **File**: src/domain/models/user.ts:89
- **Pattern**: Domain errors should be typed Effect errors
- **Current**:
```typescript
if (!email.includes('@')) {
throw new Error('Invalid email') // ❌ Untyped error
}
```
- **Suggested**:
```typescript
class InvalidEmailError extends Data.TaggedError("InvalidEmailError")<{
email: string
}> {}
if (!email.includes('@')) {
return Effect.fail(new InvalidEmailError({ email }))
}
```
- **Reference**: @docs/infrastructure/framework/effect.md#typed-errors
- **Reason**: Typed errors enable exhaustive error handling and better IDE support
[... continue for all Effect violations ...]
## React 19 (8 issues)
### 1. Unnecessary useMemo
- **Severity**: 🟡 MEDIUM
- **File**: src/presentation/components/expensive-list.tsx:24
- **Pattern**: React 19 Compiler auto-optimizes, no manual memoization needed
- **Current**:
```typescript
const filtered = useMemo(
() => items.filter(item => item.active),
[items]
)
```
- **Suggested**:
```typescript
const filtered = items.filter(item => item.active) // Let compiler optimize
```
- **Reference**: @docs/infrastructure/ui/react.md#no-manual-memoization
- **Reason**: React 19 Compiler handles memoization automatically
[... continue for all React violations ...]
## Drizzle ORM (5 issues)
### 1. Raw SQL Query
- **Severity**: 🔴 HIGH
- **File**: src/infrastructure/database/users.ts:56
- **Pattern**: Use Drizzle query builder for type safety
- **Current**:
```typescript
const users = await db.execute(`SELECT * FROM users WHERE role = '${role}'`)
```
- **Suggested**:
```typescript
const users = await db.select().from(users).where(eq(users.role, role))
```
- **Reference**: @docs/infrastructure/database/drizzle.md#type-safe-queries
- **Reason**: Query builder provides type safety and prevents SQL injection
[... continue for all Drizzle violations ...]
## Recommendations
### High Priority (Fix This Sprint)
1. Convert Application layer workflows to Effect.gen (7 locations)
2. Replace throws with typed Effect errors (3 locations)
3. Use Drizzle query builder instead of raw SQL (2 locations)
### Medium Priority (Next Sprint)
4. Remove unnecessary useMemo/useCallback (8 locations)
5. Use TanStack Query array-based keys (4 locations)
### Low Priority (Backlog)
6. Optimize Server Component usage (5 locations could be server components)
7. Add Zod resolvers to forms missing validation (3 locations)
## Next Steps
1. **Week 1**: Fix HIGH severity violations
2. **Week 2**: Address MEDIUM severity violations
3. **Week 3**: Update team documentation with common anti-patterns
4. **Week 4**: Add best-practices-checker to pre-commit hooks
```
## Communication Style
- **Framework-Specific**: Group violations by framework for context
- **Severity-Based**: Prioritize HIGH > MEDIUM > LOW
- **Code Examples**: Show current code vs. suggested code
- **Referenced**: Link to exact documentation sections
- **Reasoned**: Explain WHY best practice matters (not just WHAT)
## Limitations
- **Documentation-Based**: Only checks patterns explicitly documented
- **Pattern Matching**: Can't detect all anti-patterns (requires manual review)
- **No Execution**: Doesn't verify runtime behavior or performance
- **Static Analysis Only**: Can't detect logical errors or design flaws
- **False Positives**: May flag legitimate exceptions to best practices
## Integration Points
Use this skill:
- **With codebase-refactor-auditor**: Identify refactoring priorities
- **In code reviews**: Catch anti-patterns early
- **During onboarding**: Teach team best practices
- **Before releases**: Ensure framework compliance
**Complement with**:
- ESLint (automated enforcement)
- Manual code review (design decisions)
- Performance profiling (runtime validation)
- Framework-specific linters (e.g., eslint-plugin-react)