@malamute/ai-rules 1.0.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +272 -121
  2. package/bin/cli.js +5 -2
  3. package/configs/_shared/CLAUDE.md +52 -149
  4. package/configs/_shared/rules/conventions/documentation.md +324 -0
  5. package/configs/_shared/rules/conventions/git.md +265 -0
  6. package/configs/_shared/rules/conventions/npm.md +80 -0
  7. package/configs/_shared/{.claude/rules → rules/conventions}/performance.md +1 -1
  8. package/configs/_shared/rules/conventions/principles.md +334 -0
  9. package/configs/_shared/rules/devops/ci-cd.md +262 -0
  10. package/configs/_shared/rules/devops/docker.md +275 -0
  11. package/configs/_shared/rules/devops/nx.md +194 -0
  12. package/configs/_shared/rules/domain/backend/api-design.md +203 -0
  13. package/configs/_shared/rules/lang/csharp/async.md +220 -0
  14. package/configs/_shared/rules/lang/csharp/csharp.md +314 -0
  15. package/configs/_shared/rules/lang/csharp/linq.md +210 -0
  16. package/configs/_shared/rules/lang/python/async.md +337 -0
  17. package/configs/_shared/rules/lang/python/celery.md +476 -0
  18. package/configs/_shared/rules/lang/python/config.md +339 -0
  19. package/configs/{python/.claude/rules → _shared/rules/lang/python}/database/sqlalchemy.md +6 -1
  20. package/configs/_shared/rules/lang/python/deployment.md +523 -0
  21. package/configs/_shared/rules/lang/python/error-handling.md +330 -0
  22. package/configs/_shared/rules/lang/python/migrations.md +421 -0
  23. package/configs/_shared/rules/lang/python/python.md +172 -0
  24. package/configs/_shared/rules/lang/python/repository.md +383 -0
  25. package/configs/{python/.claude/rules → _shared/rules/lang/python}/testing.md +2 -69
  26. package/configs/_shared/rules/lang/typescript/async.md +447 -0
  27. package/configs/_shared/rules/lang/typescript/generics.md +356 -0
  28. package/configs/_shared/rules/lang/typescript/typescript.md +212 -0
  29. package/configs/_shared/rules/quality/error-handling.md +48 -0
  30. package/configs/_shared/rules/quality/logging.md +45 -0
  31. package/configs/_shared/rules/quality/observability.md +240 -0
  32. package/configs/_shared/rules/quality/testing-patterns.md +65 -0
  33. package/configs/_shared/rules/security/secrets-management.md +222 -0
  34. package/configs/_shared/skills/analysis/explore/SKILL.md +257 -0
  35. package/configs/_shared/skills/analysis/security-audit/SKILL.md +184 -0
  36. package/configs/_shared/skills/dev/api-endpoint/SKILL.md +126 -0
  37. package/configs/_shared/{.claude/commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
  38. package/configs/_shared/{.claude/commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
  39. package/configs/_shared/{.claude/commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
  40. package/configs/_shared/skills/infra/deploy/SKILL.md +139 -0
  41. package/configs/_shared/skills/infra/docker/SKILL.md +95 -0
  42. package/configs/_shared/skills/infra/migration/SKILL.md +158 -0
  43. package/configs/_shared/skills/nx/nx-affected/SKILL.md +72 -0
  44. package/configs/_shared/skills/nx/nx-lib/SKILL.md +375 -0
  45. package/configs/angular/CLAUDE.md +24 -216
  46. package/configs/angular/{.claude/rules → rules/core}/components.md +69 -15
  47. package/configs/angular/rules/core/resource.md +285 -0
  48. package/configs/angular/rules/core/signals.md +323 -0
  49. package/configs/angular/rules/http.md +338 -0
  50. package/configs/angular/rules/routing.md +291 -0
  51. package/configs/angular/rules/ssr.md +312 -0
  52. package/configs/angular/rules/state/signal-store.md +408 -0
  53. package/configs/angular/{.claude/rules → rules/state}/state.md +2 -2
  54. package/configs/angular/{.claude/rules → rules}/testing.md +7 -7
  55. package/configs/angular/rules/ui/aria.md +422 -0
  56. package/configs/angular/rules/ui/forms.md +424 -0
  57. package/configs/angular/rules/ui/pipes-directives.md +335 -0
  58. package/configs/angular/{.claude/settings.json → settings.json} +3 -0
  59. package/configs/dotnet/CLAUDE.md +53 -286
  60. package/configs/dotnet/rules/background-services.md +552 -0
  61. package/configs/dotnet/rules/configuration.md +426 -0
  62. package/configs/dotnet/rules/ddd.md +447 -0
  63. package/configs/dotnet/rules/dependency-injection.md +343 -0
  64. package/configs/dotnet/rules/mediatr.md +320 -0
  65. package/configs/dotnet/rules/middleware.md +489 -0
  66. package/configs/dotnet/rules/result-pattern.md +363 -0
  67. package/configs/dotnet/rules/validation.md +388 -0
  68. package/configs/dotnet/settings.json +29 -0
  69. package/configs/fastapi/CLAUDE.md +144 -0
  70. package/configs/fastapi/rules/background-tasks.md +254 -0
  71. package/configs/fastapi/rules/dependencies.md +170 -0
  72. package/configs/{python/.claude → fastapi}/rules/fastapi.md +61 -1
  73. package/configs/fastapi/rules/lifespan.md +274 -0
  74. package/configs/fastapi/rules/middleware.md +229 -0
  75. package/configs/fastapi/rules/pydantic.md +433 -0
  76. package/configs/fastapi/rules/responses.md +251 -0
  77. package/configs/fastapi/rules/routers.md +202 -0
  78. package/configs/fastapi/rules/security.md +222 -0
  79. package/configs/fastapi/rules/testing.md +251 -0
  80. package/configs/fastapi/rules/websockets.md +298 -0
  81. package/configs/fastapi/settings.json +35 -0
  82. package/configs/flask/CLAUDE.md +166 -0
  83. package/configs/flask/rules/blueprints.md +208 -0
  84. package/configs/flask/rules/cli.md +285 -0
  85. package/configs/flask/rules/configuration.md +281 -0
  86. package/configs/flask/rules/context.md +238 -0
  87. package/configs/flask/rules/error-handlers.md +278 -0
  88. package/configs/flask/rules/extensions.md +278 -0
  89. package/configs/flask/rules/flask.md +171 -0
  90. package/configs/flask/rules/marshmallow.md +206 -0
  91. package/configs/flask/rules/security.md +267 -0
  92. package/configs/flask/rules/testing.md +284 -0
  93. package/configs/flask/settings.json +35 -0
  94. package/configs/nestjs/CLAUDE.md +57 -215
  95. package/configs/nestjs/rules/common-patterns.md +300 -0
  96. package/configs/nestjs/rules/filters.md +376 -0
  97. package/configs/nestjs/rules/interceptors.md +317 -0
  98. package/configs/nestjs/rules/middleware.md +321 -0
  99. package/configs/nestjs/{.claude/rules → rules}/modules.md +26 -0
  100. package/configs/nestjs/rules/pipes.md +351 -0
  101. package/configs/nestjs/rules/websockets.md +451 -0
  102. package/configs/nestjs/settings.json +31 -0
  103. package/configs/nextjs/CLAUDE.md +69 -331
  104. package/configs/nextjs/rules/api-routes.md +358 -0
  105. package/configs/nextjs/rules/authentication.md +355 -0
  106. package/configs/nextjs/{.claude/rules → rules}/components.md +52 -0
  107. package/configs/nextjs/rules/data-fetching.md +249 -0
  108. package/configs/nextjs/rules/database.md +400 -0
  109. package/configs/nextjs/rules/middleware.md +303 -0
  110. package/configs/nextjs/rules/routing.md +324 -0
  111. package/configs/nextjs/rules/seo.md +350 -0
  112. package/configs/nextjs/rules/server-actions.md +353 -0
  113. package/configs/nextjs/{.claude/rules → rules}/state/zustand.md +6 -6
  114. package/configs/nextjs/{.claude/settings.json → settings.json} +7 -0
  115. package/package.json +24 -9
  116. package/src/cli.js +218 -0
  117. package/src/config.js +63 -0
  118. package/src/index.js +4 -0
  119. package/src/installer.js +414 -0
  120. package/src/merge.js +109 -0
  121. package/src/tech-config.json +45 -0
  122. package/src/utils.js +88 -0
  123. package/configs/dotnet/.claude/settings.json +0 -9
  124. package/configs/nestjs/.claude/settings.json +0 -15
  125. package/configs/python/.claude/rules/flask.md +0 -332
  126. package/configs/python/.claude/settings.json +0 -18
  127. package/configs/python/CLAUDE.md +0 -273
  128. package/src/install.js +0 -315
  129. /package/configs/_shared/{.claude/rules → rules/domain/frontend}/accessibility.md +0 -0
  130. /package/configs/_shared/{.claude/rules → rules/security}/security.md +0 -0
  131. /package/configs/_shared/{.claude/skills → skills/dev}/debug/SKILL.md +0 -0
  132. /package/configs/_shared/{.claude/skills → skills/dev}/learning/SKILL.md +0 -0
  133. /package/configs/_shared/{.claude/skills → skills/dev}/spec/SKILL.md +0 -0
  134. /package/configs/_shared/{.claude/skills → skills/git}/review/SKILL.md +0 -0
  135. /package/configs/dotnet/{.claude/rules → rules}/api.md +0 -0
  136. /package/configs/dotnet/{.claude/rules → rules}/architecture.md +0 -0
  137. /package/configs/dotnet/{.claude/rules → rules}/database/efcore.md +0 -0
  138. /package/configs/dotnet/{.claude/rules → rules}/testing.md +0 -0
  139. /package/configs/nestjs/{.claude/rules → rules}/auth.md +0 -0
  140. /package/configs/nestjs/{.claude/rules → rules}/database/prisma.md +0 -0
  141. /package/configs/nestjs/{.claude/rules → rules}/database/typeorm.md +0 -0
  142. /package/configs/nestjs/{.claude/rules → rules}/testing.md +0 -0
  143. /package/configs/nestjs/{.claude/rules → rules}/validation.md +0 -0
  144. /package/configs/nextjs/{.claude/rules → rules}/state/redux-toolkit.md +0 -0
  145. /package/configs/nextjs/{.claude/rules → rules}/testing.md +0 -0
@@ -0,0 +1,265 @@
1
+ ---
2
+ paths:
3
+ - "**/*"
4
+ ---
5
+
6
+ # Git Rules
7
+
8
+ ## Commit Messages (Conventional Commits)
9
+
10
+ Format: `type(scope): description`
11
+
12
+ ```bash
13
+ # Types
14
+ feat # New feature
15
+ fix # Bug fix
16
+ docs # Documentation only
17
+ style # Formatting, no code change
18
+ refactor # Code change, no feature/fix
19
+ perf # Performance improvement
20
+ test # Adding/updating tests
21
+ chore # Build, CI, dependencies
22
+ ```
23
+
24
+ ```bash
25
+ # Good examples
26
+ feat(auth): add OAuth2 login with Google
27
+ fix(cart): resolve race condition in checkout
28
+ refactor(api): simplify error handling middleware
29
+ perf(db): add index on users.email column
30
+ test(orders): add integration tests for payment flow
31
+ chore(deps): upgrade typescript to 5.3
32
+
33
+ # Bad examples
34
+ fix: bug fix # Too vague
35
+ updated stuff # No type, unclear
36
+ feat: Add new feature for users # Capitalized, vague
37
+ ```
38
+
39
+ ## Branch Naming
40
+
41
+ ```bash
42
+ # Pattern: type/description or type/TICKET-description
43
+ feat/user-authentication
44
+ feat/JIRA-123-oauth-login
45
+ fix/cart-total-calculation
46
+ fix/BUG-456-null-pointer
47
+ refactor/api-error-handling
48
+ chore/upgrade-dependencies
49
+ ```
50
+
51
+ ## Workflow
52
+
53
+ ```bash
54
+ # Start new feature
55
+ git checkout main
56
+ git pull --rebase
57
+ git checkout -b feat/my-feature
58
+
59
+ # Regular commits during work
60
+ git add -p # Stage interactively
61
+ git commit -m "feat(scope): description"
62
+
63
+ # Before pushing - rebase on main
64
+ git fetch origin
65
+ git rebase origin/main
66
+
67
+ # Push (first time)
68
+ git push -u origin feat/my-feature
69
+
70
+ # Push (subsequent)
71
+ git push
72
+ ```
73
+
74
+ ## Rebase vs Merge
75
+
76
+ ```bash
77
+ # ALWAYS rebase local changes on remote
78
+ git pull --rebase origin main
79
+
80
+ # NEVER merge main into feature branch
81
+ git merge main # Creates ugly merge commits
82
+
83
+ # Interactive rebase to clean up commits before PR
84
+ git rebase -i HEAD~3
85
+ ```
86
+
87
+ ## Interactive Rebase
88
+
89
+ ```bash
90
+ # Clean up last 3 commits
91
+ git rebase -i HEAD~3
92
+
93
+ # In editor:
94
+ pick abc1234 feat(auth): add login endpoint
95
+ squash def5678 fix typo # Squash into previous
96
+ fixup ghi9012 more fixes # Squash, discard message
97
+ reword jkl3456 wip # Edit commit message
98
+ ```
99
+
100
+ ## Stashing
101
+
102
+ ```bash
103
+ # Save work in progress
104
+ git stash push -m "WIP: feature description"
105
+
106
+ # List stashes
107
+ git stash list
108
+
109
+ # Apply and drop
110
+ git stash pop
111
+
112
+ # Apply specific stash
113
+ git stash apply stash@{2}
114
+
115
+ # Drop stash
116
+ git stash drop stash@{0}
117
+ ```
118
+
119
+ ## Undoing Changes
120
+
121
+ ```bash
122
+ # Unstage file (keep changes)
123
+ git restore --staged file.ts
124
+
125
+ # Discard local changes (DESTRUCTIVE)
126
+ git restore file.ts
127
+
128
+ # Undo last commit (keep changes staged)
129
+ git reset --soft HEAD~1
130
+
131
+ # Undo last commit (keep changes unstaged)
132
+ git reset HEAD~1
133
+
134
+ # Completely undo last commit (DESTRUCTIVE)
135
+ git reset --hard HEAD~1
136
+
137
+ # Create new commit that undoes previous
138
+ git revert abc1234
139
+ ```
140
+
141
+ ## Viewing History
142
+
143
+ ```bash
144
+ # Compact log
145
+ git log --oneline -20
146
+
147
+ # With graph
148
+ git log --oneline --graph --all
149
+
150
+ # Changes in commit
151
+ git show abc1234
152
+
153
+ # Who changed this line
154
+ git blame file.ts
155
+
156
+ # Search commits by message
157
+ git log --grep="fix auth"
158
+
159
+ # Search commits by code change
160
+ git log -S "functionName" --oneline
161
+ ```
162
+
163
+ ## Cherry-Pick
164
+
165
+ ```bash
166
+ # Apply specific commit to current branch
167
+ git cherry-pick abc1234
168
+
169
+ # Cherry-pick without committing
170
+ git cherry-pick --no-commit abc1234
171
+
172
+ # Cherry-pick range
173
+ git cherry-pick abc1234..def5678
174
+ ```
175
+
176
+ ## Tags
177
+
178
+ ```bash
179
+ # Create annotated tag
180
+ git tag -a v1.2.0 -m "Release 1.2.0"
181
+
182
+ # Push tags
183
+ git push origin v1.2.0
184
+ git push origin --tags
185
+
186
+ # List tags
187
+ git tag -l "v1.*"
188
+
189
+ # Delete tag
190
+ git tag -d v1.2.0
191
+ git push origin :refs/tags/v1.2.0
192
+ ```
193
+
194
+ ## Hooks (Husky)
195
+
196
+ ```bash
197
+ # .husky/pre-commit
198
+ npm run lint-staged
199
+
200
+ # .husky/commit-msg
201
+ npx commitlint --edit $1
202
+
203
+ # .husky/pre-push
204
+ npm run test
205
+ ```
206
+
207
+ ## .gitignore Essentials
208
+
209
+ ```gitignore
210
+ # Dependencies
211
+ node_modules/
212
+ .venv/
213
+ vendor/
214
+
215
+ # Build
216
+ dist/
217
+ build/
218
+ *.dll
219
+ *.exe
220
+
221
+ # IDE
222
+ .idea/
223
+ .vscode/
224
+ *.swp
225
+
226
+ # Environment
227
+ .env
228
+ .env.local
229
+ *.local
230
+
231
+ # Secrets (NEVER commit)
232
+ *.pem
233
+ *.key
234
+ credentials.json
235
+ secrets.yaml
236
+
237
+ # OS
238
+ .DS_Store
239
+ Thumbs.db
240
+
241
+ # Logs
242
+ *.log
243
+ logs/
244
+
245
+ # Test
246
+ coverage/
247
+ .nyc_output/
248
+ ```
249
+
250
+ ## PR Best Practices
251
+
252
+ ```bash
253
+ # Keep PRs small and focused
254
+ # - One feature/fix per PR
255
+ # - <400 lines changed ideal
256
+ # - Split large changes into stacked PRs
257
+
258
+ # Before creating PR
259
+ git rebase -i origin/main # Clean history
260
+ npm run lint # Pass lint
261
+ npm run test # Pass tests
262
+
263
+ # PR title follows commit convention
264
+ feat(auth): add OAuth2 login with Google
265
+ ```
@@ -0,0 +1,80 @@
1
+ ---
2
+ paths:
3
+ - "**/package.json"
4
+ ---
5
+
6
+ # npm Conventions
7
+
8
+ ## Version Pinning
9
+
10
+ **Always use exact versions** - no `^` or `~` prefixes.
11
+
12
+ ```json
13
+ // GOOD
14
+ {
15
+ "dependencies": {
16
+ "express": "4.18.2",
17
+ "lodash": "4.17.21"
18
+ }
19
+ }
20
+
21
+ // BAD
22
+ {
23
+ "dependencies": {
24
+ "express": "^4.18.2",
25
+ "lodash": "~4.17.21"
26
+ }
27
+ }
28
+ ```
29
+
30
+ ### Why?
31
+
32
+ - **Reproducible builds** across environments
33
+ - **No surprise breaking changes** from minor/patch updates
34
+ - **Lock file is source of truth** but pinning adds defense in depth
35
+ - **Explicit upgrades** via `npm update` or renovate/dependabot
36
+
37
+ ### Commands
38
+
39
+ ```bash
40
+ # Install with exact version
41
+ npm install express --save-exact
42
+
43
+ # Configure npm to always save exact
44
+ npm config set save-exact true
45
+
46
+ # Or in .npmrc
47
+ save-exact=true
48
+ ```
49
+
50
+ ## Scripts
51
+
52
+ Use consistent script names:
53
+
54
+ ```json
55
+ {
56
+ "scripts": {
57
+ "dev": "...",
58
+ "build": "...",
59
+ "start": "...",
60
+ "test": "...",
61
+ "test:watch": "...",
62
+ "test:cov": "...",
63
+ "lint": "...",
64
+ "lint:fix": "...",
65
+ "format": "..."
66
+ }
67
+ }
68
+ ```
69
+
70
+ ## Engine Requirements
71
+
72
+ Specify Node.js version:
73
+
74
+ ```json
75
+ {
76
+ "engines": {
77
+ "node": ">=20.0.0"
78
+ }
79
+ }
80
+ ```
@@ -79,7 +79,7 @@ await cache.delete(`user:${userId}`);
79
79
  ### Memoization
80
80
  ```typescript
81
81
  // Memoize pure functions
82
- const memoize = <T>(fn: (...args: any[]) => T): ((...args: any[]) => T) => {
82
+ const memoize = <T, Args extends unknown[]>(fn: (...args: Args) => T): ((...args: Args) => T) => {
83
83
  const cache = new Map();
84
84
  return (...args) => {
85
85
  const key = JSON.stringify(args);
@@ -0,0 +1,334 @@
1
+ ---
2
+ paths:
3
+ - "**/*.ts"
4
+ - "**/*.tsx"
5
+ - "**/*.js"
6
+ - "**/*.py"
7
+ - "**/*.cs"
8
+ ---
9
+
10
+ # Software Engineering Principles
11
+
12
+ ## YAGNI - You Aren't Gonna Need It
13
+
14
+ Don't implement features until they're actually needed.
15
+
16
+ ```typescript
17
+ // BAD - Building for hypothetical future
18
+ interface UserService {
19
+ getUser(id: string): User;
20
+ getUserWithCache(id: string, ttl?: number): User;
21
+ getUserAsync(id: string): Promise<User>;
22
+ getUserBatch(ids: string[]): User[];
23
+ getUserByEmail(email: string): User;
24
+ getUserByPhone(phone: string): User; // No one asked for this
25
+ getUserBySSN(ssn: string): User; // No one asked for this
26
+ }
27
+
28
+ // GOOD - Only what's needed now
29
+ interface UserService {
30
+ getUser(id: string): Promise<User>;
31
+ getUserByEmail(email: string): Promise<User>;
32
+ }
33
+ ```
34
+
35
+ ```typescript
36
+ // BAD - Premature abstraction
37
+ class AbstractDataProcessor<T, R, C extends Config> {
38
+ // 200 lines of "flexible" code no one uses
39
+ }
40
+
41
+ // GOOD - Concrete implementation
42
+ function processUserData(users: User[]): ProcessedUser[] {
43
+ return users.map(u => ({ ...u, fullName: `${u.first} ${u.last}` }));
44
+ }
45
+ ```
46
+
47
+ ## KISS - Keep It Simple, Stupid
48
+
49
+ Choose the simplest solution that works.
50
+
51
+ ```typescript
52
+ // BAD - Over-engineered
53
+ class StringUtils {
54
+ private static instance: StringUtils;
55
+ private constructor() {}
56
+
57
+ static getInstance(): StringUtils {
58
+ if (!this.instance) this.instance = new StringUtils();
59
+ return this.instance;
60
+ }
61
+
62
+ capitalize(str: string): string {
63
+ return str.charAt(0).toUpperCase() + str.slice(1);
64
+ }
65
+ }
66
+ // Usage: StringUtils.getInstance().capitalize('hello')
67
+
68
+ // GOOD - Simple function
69
+ function capitalize(str: string): string {
70
+ return str.charAt(0).toUpperCase() + str.slice(1);
71
+ }
72
+ // Usage: capitalize('hello')
73
+ ```
74
+
75
+ ```typescript
76
+ // BAD - Unnecessary complexity
77
+ const isAdult = (age: number) =>
78
+ new AgeValidator(new AgePolicy(18)).validate(age).isValid();
79
+
80
+ // GOOD - Direct and clear
81
+ const isAdult = (age: number) => age >= 18;
82
+ ```
83
+
84
+ ## SOLID Principles
85
+
86
+ ### S - Single Responsibility
87
+
88
+ A class/function should have only one reason to change.
89
+
90
+ ```typescript
91
+ // BAD - Multiple responsibilities
92
+ class UserService {
93
+ createUser(data: UserData) { /* ... */ }
94
+ sendWelcomeEmail(user: User) { /* ... */ }
95
+ generatePDF(user: User) { /* ... */ }
96
+ validateCreditCard(card: Card) { /* ... */ }
97
+ }
98
+
99
+ // GOOD - Single responsibility each
100
+ class UserService {
101
+ createUser(data: UserData) { /* ... */ }
102
+ }
103
+
104
+ class EmailService {
105
+ sendWelcomeEmail(user: User) { /* ... */ }
106
+ }
107
+
108
+ class PDFService {
109
+ generateUserReport(user: User) { /* ... */ }
110
+ }
111
+ ```
112
+
113
+ ### O - Open/Closed
114
+
115
+ Open for extension, closed for modification.
116
+
117
+ ```typescript
118
+ // BAD - Must modify to add new types
119
+ function calculateArea(shape: Shape) {
120
+ if (shape.type === 'circle') {
121
+ return Math.PI * shape.radius ** 2;
122
+ } else if (shape.type === 'rectangle') {
123
+ return shape.width * shape.height;
124
+ }
125
+ // Must add new else-if for each shape
126
+ }
127
+
128
+ // GOOD - Extend without modifying
129
+ interface Shape {
130
+ area(): number;
131
+ }
132
+
133
+ class Circle implements Shape {
134
+ constructor(private radius: number) {}
135
+ area() { return Math.PI * this.radius ** 2; }
136
+ }
137
+
138
+ class Rectangle implements Shape {
139
+ constructor(private width: number, private height: number) {}
140
+ area() { return this.width * this.height; }
141
+ }
142
+
143
+ // Add new shapes without changing existing code
144
+ class Triangle implements Shape {
145
+ constructor(private base: number, private height: number) {}
146
+ area() { return 0.5 * this.base * this.height; }
147
+ }
148
+ ```
149
+
150
+ ### L - Liskov Substitution
151
+
152
+ Subtypes must be substitutable for their base types.
153
+
154
+ ```typescript
155
+ // BAD - Violates LSP
156
+ class Bird {
157
+ fly() { /* ... */ }
158
+ }
159
+
160
+ class Penguin extends Bird {
161
+ fly() { throw new Error("Can't fly!"); } // Breaks substitutability
162
+ }
163
+
164
+ // GOOD - Proper hierarchy
165
+ interface Bird {
166
+ move(): void;
167
+ }
168
+
169
+ class FlyingBird implements Bird {
170
+ move() { this.fly(); }
171
+ private fly() { /* ... */ }
172
+ }
173
+
174
+ class Penguin implements Bird {
175
+ move() { this.swim(); }
176
+ private swim() { /* ... */ }
177
+ }
178
+ ```
179
+
180
+ ### I - Interface Segregation
181
+
182
+ Clients shouldn't depend on interfaces they don't use.
183
+
184
+ ```typescript
185
+ // BAD - Fat interface
186
+ interface Worker {
187
+ work(): void;
188
+ eat(): void;
189
+ sleep(): void;
190
+ attendMeeting(): void;
191
+ writeReport(): void;
192
+ }
193
+
194
+ // GOOD - Segregated interfaces
195
+ interface Workable {
196
+ work(): void;
197
+ }
198
+
199
+ interface Meetable {
200
+ attendMeeting(): void;
201
+ }
202
+
203
+ interface Reportable {
204
+ writeReport(): void;
205
+ }
206
+
207
+ class Developer implements Workable, Meetable {
208
+ work() { /* ... */ }
209
+ attendMeeting() { /* ... */ }
210
+ }
211
+
212
+ class Robot implements Workable {
213
+ work() { /* ... */ }
214
+ // Doesn't need eat, sleep, or meetings
215
+ }
216
+ ```
217
+
218
+ ### D - Dependency Inversion
219
+
220
+ Depend on abstractions, not concretions.
221
+
222
+ ```typescript
223
+ // BAD - Depends on concrete implementation
224
+ class OrderService {
225
+ private db = new MySQLDatabase();
226
+ private mailer = new SendGridMailer();
227
+
228
+ createOrder(order: Order) {
229
+ this.db.save(order);
230
+ this.mailer.send(order.userEmail, 'Order confirmed');
231
+ }
232
+ }
233
+
234
+ // GOOD - Depends on abstractions
235
+ class OrderService {
236
+ constructor(
237
+ private repository: OrderRepository,
238
+ private notifier: Notifier,
239
+ ) {}
240
+
241
+ createOrder(order: Order) {
242
+ this.repository.save(order);
243
+ this.notifier.notify(order.userEmail, 'Order confirmed');
244
+ }
245
+ }
246
+
247
+ // Can inject any implementation
248
+ new OrderService(new PostgresOrderRepo(), new EmailNotifier());
249
+ new OrderService(new MongoOrderRepo(), new SMSNotifier());
250
+ ```
251
+
252
+ ## SoC - Separation of Concerns
253
+
254
+ Separate code into distinct sections, each handling a specific concern.
255
+
256
+ ```typescript
257
+ // BAD - Mixed concerns
258
+ async function handleUserRegistration(req: Request, res: Response) {
259
+ // Validation
260
+ if (!req.body.email.includes('@')) {
261
+ return res.status(400).json({ error: 'Invalid email' });
262
+ }
263
+
264
+ // Business logic
265
+ const hashedPassword = await bcrypt.hash(req.body.password, 10);
266
+
267
+ // Data access
268
+ const user = await db.query(
269
+ 'INSERT INTO users (email, password) VALUES ($1, $2)',
270
+ [req.body.email, hashedPassword]
271
+ );
272
+
273
+ // Presentation
274
+ return res.json({ id: user.id, email: user.email });
275
+ }
276
+
277
+ // GOOD - Separated concerns
278
+ // validation.ts
279
+ const userSchema = z.object({
280
+ email: z.string().email(),
281
+ password: z.string().min(8),
282
+ });
283
+
284
+ // user.service.ts
285
+ class UserService {
286
+ async register(data: CreateUserDto): Promise<User> {
287
+ const hashedPassword = await this.hashPassword(data.password);
288
+ return this.userRepository.create({ ...data, password: hashedPassword });
289
+ }
290
+ }
291
+
292
+ // user.controller.ts
293
+ async function register(req: Request, res: Response) {
294
+ const data = userSchema.parse(req.body);
295
+ const user = await userService.register(data);
296
+ return res.json(toUserResponse(user));
297
+ }
298
+ ```
299
+
300
+ ## DRY - Don't Repeat Yourself
301
+
302
+ But don't over-apply it. Duplication is better than the wrong abstraction.
303
+
304
+ ```typescript
305
+ // BAD - Premature DRY (wrong abstraction)
306
+ function processEntity(entity: User | Product | Order, action: string) {
307
+ // 100 lines of if/else handling all cases
308
+ }
309
+
310
+ // GOOD - Some duplication is OK when contexts differ
311
+ function processUser(user: User) { /* user-specific logic */ }
312
+ function processProduct(product: Product) { /* product-specific logic */ }
313
+ function processOrder(order: Order) { /* order-specific logic */ }
314
+
315
+ // GOOD - DRY when truly duplicated
316
+ function formatCurrency(amount: number): string {
317
+ return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount);
318
+ }
319
+ // Use everywhere instead of repeating the formatting logic
320
+ ```
321
+
322
+ ## Summary
323
+
324
+ | Principle | Remember |
325
+ |-----------|----------|
326
+ | YAGNI | Build what you need now, not what you might need |
327
+ | KISS | Simple > Clever. If it's hard to explain, simplify it |
328
+ | SRP | One reason to change per class/function |
329
+ | OCP | Add new code, don't modify existing code |
330
+ | LSP | Subtypes must honor parent contracts |
331
+ | ISP | Small, focused interfaces > large, general ones |
332
+ | DIP | Inject dependencies, don't instantiate them |
333
+ | SoC | Validation, business logic, data access = separate |
334
+ | DRY | Avoid duplication, but not at the cost of clarity |