Comprehensive Rules to eliminate code duplication in JavaScript/TypeScript web projects.
Tired of maintaining identical functions scattered across your codebase? Spending hours debugging the same logic in six different files? Your development velocity is being crushed by code duplication, and every "quick copy-paste" is technical debt that compounds daily.
Code duplication isn't just about file size—it's a productivity killer that creates cascading problems:
Real scenario: A payment validation function exists in 8 different components. A security vulnerability requires an urgent patch. You find and fix 7 instances. The 8th one ships to production. Sound familiar?
These Cursor Rules implement a comprehensive deduplication strategy that catches duplicates before they ship, extracts reusable patterns automatically, and maintains code quality through automated scanning. Instead of playing whack-a-mole with duplicate code, you'll build a self-maintaining codebase that scales cleanly.
The rules integrate battle-tested tools (SonarQube, ESLint plugins) with intelligent refactoring patterns, giving you both prevention and cure for code duplication.
Reduce Debugging Time by 60%: Fix bugs once, everywhere. No more hunting through similar-looking functions wondering which one has the latest fix.
Cut Feature Development Time in Half: Add new functionality to centralized utilities instead of copy-pasting and modifying across multiple files.
Eliminate "Copy-Paste" Code Reviews: Automated scanning catches duplicates in CI, so code reviews focus on logic instead of spotting similarities.
Improve Code Coverage Without Writing More Tests: Extract shared logic into testable utilities—one test suite covers multiple use cases.
// Component A
const formatUserDate = (date: Date): string => {
return date.toLocaleDateString('en-US', {
year: 'numeric', month: 'short', day: 'numeric'
});
};
// Component B
const displayDate = (date: Date): string => {
return date.toLocaleDateString('en-US', {
year: 'numeric', month: 'short', day: 'numeric'
});
};
// Component C
const showFormattedDate = (date: Date): string => {
return date.toLocaleDateString('en-US', {
year: 'numeric', month: 'short', day: 'numeric'
});
};
Adding timezone support means updating 47 similar functions. Miss one, break production.
// @/utils/date.ts
export const formatDate = (date: Date, options?: Intl.DateTimeFormatOptions): string => {
const defaultOptions = { year: 'numeric', month: 'short', day: 'numeric' };
return date.toLocaleDateString('en-US', { ...defaultOptions, ...options });
};
// Usage everywhere
import { formatDate } from '@/utils/date';
const displayText = formatDate(userDate); // Add timezone support once, works everywhere
One function, one test suite, one place to add features. Your SonarQube dashboard shows zero duplicated blocks.
Before: Exception handling copy-pasted across API calls
// In 15 different service files
try {
const result = await apiCall();
return result.data;
} catch (error) {
console.error('API call failed:', error);
if (error.status === 401) {
redirectToLogin();
}
throw new Error('Something went wrong');
}
After: Centralized error handling with typed exceptions
// @/utils/errors.ts
export class APIError extends Error {
constructor(message: string, public code: string, public originalError?: Error) {
super(message);
this.stack = originalError?.stack;
}
}
export const withErrorHandling = <T>(apiCall: () => Promise<T>) => async (): Promise<T> => {
try {
return await apiCall();
} catch (error) {
if (error.status === 401) redirectToLogin();
throw new APIError('API call failed', 'API_ERROR', error);
}
};
// Usage
const fetchUser = withErrorHandling(() => api.get('/user'));
Add SonarQube integration to your CI pipeline:
# .github/workflows/quality.yml
- name: SonarQube Scan
uses: SonarSource/sonarcloud-github-action@v1
with:
args: >
-Dsonar.duplicatedLines.threshold=3
-Dsonar.duplicatedBlocks.threshold=2
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
// .eslintrc.js
{
"extends": ["plugin:sonarjs/recommended"],
"rules": {
"sonarjs/no-duplicate-string": ["error", { "threshold": 3 }],
"sonarjs/no-identical-functions": "error",
"no-duplicate-imports": "error"
}
}
Copy the rules into your .cursorrules file and restart Cursor. The AI will now:
Configure your CI to fail on new duplication:
// sonar-project.properties
sonar.qualitygate.wait=true
sonar.coverage.exclusions=**/*.test.ts
sonar.cpd.exclusions=**/types.ts
Week 1: Cursor identifies 23 duplicate functions in your React components. You extract them into 6 reusable hooks. Code review time drops by 40%.
Month 1: Your SonarQube dashboard shows 90% reduction in duplicated blocks. Bug fix deployments go from 4 files changed to 1 file changed.
Month 3: New features require 50% fewer lines of code because you're composing existing utilities instead of writing from scratch. Your test coverage improves because shared utilities have dedicated test suites.
Developer Feedback: "I used to dread refactoring because I never knew what I'd break. Now the scanner catches duplicates automatically, and I can extract common patterns confidently."
Your codebase becomes self-improving—each extraction makes future development faster. Instead of fighting technical debt, you're building reusable assets that accelerate every subsequent feature.
Stop writing the same code twice. Your future self will thank you, your code reviewers will thank you, and your production deployments will be more reliable. The rules are waiting in your .cursorrules file.
You are an expert in JavaScript, TypeScript, Node.js, React, Jest, ESLint, SonarQube, Codacy, GitHub Actions, and modern refactoring tooling.
Key Principles
- Follow DRY (Don’t Repeat Yourself); each piece of knowledge lives in exactly one place.
- Prefer composition over duplication; break logic into atomic, reusable functions or hooks.
- Integrate automated duplication scanners (SonarQube, Codacy) in every PR via CI.
- Treat "copy-paste" code as a defect; create a tracking issue before merging.
- Balance DRY with readability: never hide intent behind over-generic abstractions.
- Enforce peer reviews with side-by-side diff for suspiciously similar code blocks.
- Refactor only with complete test coverage; never mix behaviour change & deduplication in one commit.
JavaScript / TypeScript
- Use TypeScript for all new files; enable "noImplicitAny" & "exactOptionalPropertyTypes" to expose duplicate type definitions.
- Encapsulate reusable logic in pure functions: `export const formatDate = (d: Date): string => …`.
- Prefer higher-order functions instead of repeating loop bodies:
```ts
const withRetry = <T>(fn: () => Promise<T>) => async () => { … }
```
- Extract constants/enums instead of re-typing literals across files.
- Keep one public function per file (`x.ts`), colocate `types.ts` and `helpers.ts` when size > 300 LOC.
- Import paths must be absolute (`@/utils/date`) to centralise usage.
- Disallow identical template strings; move to `templates/` directory.
Error Handling & Validation
- Fail fast: validate inputs at the top of the deduplicated function.
- Throw typed errors (`class ValidationError extends Error { code = 'VALIDATION' }`).
- Ensure wrapper utilities propagate original stack traces: `throw new WrappedError('msg', err)`.
- Surface user-friendly messages only from a central error handler – never inline.
- Include exhaustive `switch` to catch newly added enum members; duplication scanners can’t flag missing branches.
Framework-Specific Rules
React
- Use functional components + hooks; extract shared UI logic into custom hooks (`usePagination`) instead of copy-pasting between components.
- Co-locate variants in `components/common/` and export via index barrel.
- Avoid duplicate inline styles; centralise with CSS Module or Tailwind class composition.
Node.js / Express
- Move repeated middleware to `middlewares/` and reference by array spread: `app.use(...standardMiddlewares)`.
- Share validation schemas with the client (e.g., `zod` shared package) to avoid duplicated shape checking.
Additional Sections
Testing
- Write unit tests for every extracted helper; minimum branch coverage 90 %.
- Use snapshot tests cautiously; they can hide duplication. Prefer explicit assertions.
- When refactoring, pin behavioural tests first (`git tag before-dedup`), refactor, then ensure tests still pass.
Performance
- Benchmark new abstraction with `benchmark.js`; reject PR if p95 latency regresses >5 %.
- Cache pure function results with memoization (`import memoize from 'lodash/memoize'`) when used >1 000 calls/s.
Tooling & CI
- Add `sonar.duplicateCode.enabled=true` to SonarQube project properties.
- Github Actions step:
```yaml
- name: Scan Duplicate Code
uses: SonarSource/sonarcloud-github-action@v1
```
- ESLint rule set: enable `no-duplicate-imports`, `sonarjs/no-identical-functions`, `no-useless-rename`.
- Configure `duplicate-code-finder` Gradle plugin for mono-repos containing non-JS modules.
Naming Conventions
- Functions: verb + noun (`calculateTax`, `sendEmail`).
- Shared hooks: `use` prefix (`useDebounce`).
- Modules exposing deduplicated APIs use singular nouns (`logger`, `mailer`).
Commit Etiquette
- Deduplication commits: `refactor(scope): extract <name> from <formerly duplicated area>`.
- Link to original duplication detection report in commit message footer.
Common Pitfalls & How to Avoid
- Over-generic functions with many conditional branches → split by responsibility.
- Silent behaviour changes during refactor → enforce snapshot & integration test diff.
- Forgetting to delete old duplicate code after extraction → CI fails if duplicates still present.
Checklist (pre-merge)
[ ] SonarQube shows 0 new duplicated blocks.
[ ] Unit + integration tests green.
[ ] Function/variable names communicate intent.
[ ] Happy path unaffected (manual smoke test).
[ ] Documentation updated with new abstraction location.