Agent skill
pattern-enforcement
Enforce architectural patterns with ESLint rules. Block infra imports, enforce object params, prevent server/client leaks.
Install this agent skill to your Project
npx add-skill https://github.com/jagreehal/jagreehal-claude-skills/tree/main/skills/pattern-enforcement
SKILL.md
Enforcing Patterns with ESLint
Core Principle
Documentation is a ritual. Rules are enforcement. Patterns without rules that fail the build are just suggestions.
Required ESLint Rules
1. Enforce Architectural Boundaries
Domain code must NOT import from infrastructure:
// WRONG
import { db } from '../infra/database';
// CORRECT - Inject dependency
async function getUser(args, deps: { db: Database }) {
return deps.db.findUser(args.userId);
}
Simple approach - Use no-restricted-imports:
// eslint.config.mjs
export default {
rules: {
"no-restricted-imports": [
"error",
{
patterns: [{
group: ["**/infra/**"],
message: "Domain code must not import from infra. Inject dependencies instead.",
}],
},
],
},
};
Better approach - Use eslint-plugin-boundaries for directional rules:
npm install -D eslint-plugin-boundaries
import boundaries from 'eslint-plugin-boundaries';
export default [{
plugins: { boundaries },
settings: {
'boundaries/elements': [
{ type: 'domain', pattern: 'src/domain/**' },
{ type: 'infra', pattern: 'src/infra/**' },
{ type: 'api', pattern: 'src/api/**' },
],
},
rules: {
'boundaries/element-types': ['error', {
default: 'disallow',
rules: [
{ from: 'domain', allow: ['domain'] }, // Domain is pure
{ from: 'infra', allow: ['domain', 'infra'] }, // Infra implements domain
{ from: 'api', allow: ['domain', 'infra', 'api'] }, // API wires everything
],
}],
},
}];
2. Enforce Function Signatures
Functions should use object parameters, not positional:
// WRONG - Positional parameters
function createUser(name: string, email: string, age: number) { }
// CORRECT - Object parameter
function createUser(args: { name: string; email: string; age: number }) { }
npm install -D eslint-plugin-prefer-object-params
import preferObjectParams from 'eslint-plugin-prefer-object-params';
export default [{
plugins: { 'prefer-object-params': preferObjectParams },
rules: {
'prefer-object-params/prefer-object-params': 'error',
},
}];
Rule ignores single-parameter functions, constructors, and test files by default.
3. Enforce Server/Client Boundaries
Prevent server code from leaking to client bundles:
npm install -D eslint-plugin-no-server-imports
import noServerImports from 'eslint-plugin-no-server-imports';
export default [{
plugins: { 'no-server-imports': noServerImports },
rules: {
'no-server-imports/no-server-imports': ['error', {
serverFilePatterns: [
'**/*.server.ts',
'**/*.server.tsx',
'**/server/**',
'**/api/**',
],
}],
},
}];
Also block Node.js modules in client code:
export default [{
files: ['src/components/**/*.tsx', 'src/hooks/**/*.ts'],
rules: {
'import/no-nodejs-modules': ['error', { allow: [] }],
},
}];
Complete ESLint Config
// eslint.config.mjs
import js from '@eslint/js';
import tseslint from '@typescript-eslint/eslint-plugin';
import tsparser from '@typescript-eslint/parser';
import preferObjectParams from 'eslint-plugin-prefer-object-params';
import globals from 'globals';
export default [
js.configs.recommended,
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
project: './tsconfig.json',
},
globals: { ...globals.node, ...globals.es2022 },
},
plugins: {
'@typescript-eslint': tseslint,
'prefer-object-params': preferObjectParams,
},
rules: {
...tseslint.configs.recommended.rules,
// Architectural boundaries
'no-restricted-imports': ['error', {
patterns: [{
group: ['**/infra/**'],
message: 'Domain code must not import from infra.',
}],
}],
// Function signatures
'prefer-object-params/prefer-object-params': 'error',
// TypeScript
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-explicit-any': 'warn',
// Code quality
'prefer-const': 'error',
'no-var': 'error',
'object-shorthand': 'error',
'prefer-template': 'error',
},
},
{
files: ['**/*.test.ts', '**/*.spec.ts'],
rules: {
'prefer-object-params/prefer-object-params': 'off',
},
},
];
Essential Plugins
| Plugin | Purpose |
|---|---|
@typescript-eslint/eslint-plugin |
TypeScript-aware rules |
eslint-plugin-import |
Broken imports, circular deps |
eslint-plugin-unused-imports |
Auto-remove dead imports |
eslint-plugin-unicorn |
Modern, safer patterns |
eslint-plugin-boundaries |
Architectural boundaries |
eslint-plugin-prefer-object-params |
Object parameters |
Migrating Existing Codebases
The prefer-object-params rule currently reports violations but doesn't auto-fix them (the transformation is too complex for safe automation—call sites need updating too).
For large-scale migrations, consider:
- Incremental adoption: Start with
'warn'and fix violations file-by-file - Codemod scripts: Use jscodeshift to automate the transformation:
// transform-to-object-params.js (jscodeshift)
export default function transformer(file, api) {
const j = api.jscodeshift;
// Transform function declarations with 2+ params to object pattern
// ... (custom logic for your codebase)
}
npx jscodeshift -t transform-to-object-params.js src/**/*.ts
- AI-assisted refactoring: Modern coding agents can batch-refactor functions when given clear rules
The key is that the ESLint rule catches violations. The migration strategy is separate from enforcement.
Why Rules Matter for AI-Generated Code
Prompting is probabilistic - AI might follow patterns, might not. Rules are deterministic - lint fails, code fails, agent fixes.
If AI is writing code in your repo, constrain it with the same systems you already trust: linters, types, tests, and CI checks.
The Rules
- Enforce architectural boundaries - Block domain importing infra
- Enforce function signatures - Require object parameters
- Enforce server/client separation - Block Node.js in client code
- Use 'error' not 'warn' - Violations must fail the build
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
skill-authoring
Use when creating, editing, or reviewing skills. Covers discovery optimization, structure patterns, testing approaches, and format decisions.
testing-strategy
Test pyramid approach with unit, integration, and load tests. DI enables testability. Use vitest-mock-extended for typed mocks.
api-design
Build production-ready HTTP APIs with clean handlers, consistent error envelopes, health checks, CORS, and operational excellence.
validation-boundary
Validate at the boundary with Zod schemas and branded types. Business functions trust validated input.
branch-completion
Use when implementation is complete and tests pass. Guides completion by presenting structured options for merge, PR, or cleanup.
resilience
Add retry, timeout, and circuit breaker patterns at the workflow level. Business functions stay clean.
Didn't find tool you were looking for?