@use-the-fork/spindle 0.0.0-stage → 0.0.1
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.
- package/LICENSE +21 -0
- package/README.md +3 -2
- package/build/Assets/Blueprints/Catalog.js +110 -0
- package/build/Assets/Blueprints/agents/general.agent.md +67 -0
- package/build/Assets/Blueprints/agents/sdd-code-reviewer.agent.md +62 -0
- package/build/Assets/Blueprints/agents/sdd-code-slop-reviewer.agent.md +59 -0
- package/build/Assets/Blueprints/agents/sdd-implementer.agent.md +77 -0
- package/build/Assets/Blueprints/agents/sdd-orchestrator.agent.md +42 -0
- package/build/Assets/Blueprints/agents/sdd-re-review.agent.md +57 -0
- package/build/Assets/Blueprints/agents/sdd-reviewer.agent.md +89 -0
- package/build/Assets/Blueprints/agents/sdd-task-reviewer.agent.md +54 -0
- package/build/Assets/Blueprints/agents/shared-codebase-analyzer.agent.md +151 -0
- package/build/Assets/Blueprints/agents/shared-codebase-locator.agent.md +130 -0
- package/build/Assets/Blueprints/agents/shared-codebase-pattern-finder.agent.md +247 -0
- package/build/Assets/Blueprints/agents/shared-explore.agent.md +53 -0
- package/build/Assets/Blueprints/agents/shared-r.agent.md +17 -0
- package/build/Assets/Blueprints/agents/shared-rw.agent.md +17 -0
- package/build/Assets/Blueprints/agents/shared-rwx.agent.md +17 -0
- package/build/Assets/Blueprints/agents/shared-web-search-researcher.agent.md +122 -0
- package/build/Assets/Blueprints/instructions/code-commenting.instructions.md +39 -0
- package/build/Assets/Blueprints/instructions/source-architecture.instructions.md +15 -0
- package/build/Assets/Blueprints/skills/asd-ste100/CREDITS.MD +1 -0
- package/build/Assets/Blueprints/skills/asd-ste100/SKILL.md +83 -0
- package/build/Assets/Blueprints/skills/asd-ste100/examples/before-after.md +107 -0
- package/build/Assets/Blueprints/skills/asd-ste100/references/writing-rules.md +65 -0
- package/build/Assets/Blueprints/skills/author-agent/SKILL.md +26 -0
- package/build/Assets/Blueprints/skills/author-design/SKILL.md +38 -0
- package/build/Assets/Blueprints/skills/author-instruction/SKILL.md +28 -0
- package/build/Assets/Blueprints/skills/author-skill/SKILL.md +26 -0
- package/build/Assets/Blueprints/skills/grill/SKILL.md +33 -0
- package/build/Assets/Blueprints/skills/grill-design/SKILL.md +41 -0
- package/build/Assets/Blueprints/skills/grill-with-docs/SKILL.md +14 -0
- package/build/Assets/Blueprints/skills/implement/SKILL.md +79 -0
- package/build/Assets/Blueprints/skills/implement-spec/SKILL.md +34 -0
- package/build/Assets/Blueprints/skills/improve-codebase-architecture/HTML-REPORT.md +129 -0
- package/build/Assets/Blueprints/skills/improve-codebase-architecture/SKILL.md +74 -0
- package/build/Assets/Blueprints/skills/int-code-review/SKILL.md +76 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/SKILL.md +238 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/_sections.md +36 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/_template.md +42 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-closing-brace-labels.md +112 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-empty-docblocks.md +132 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-narration.md +118 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-placeholder.md +130 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/defensive-generic-catch.md +153 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/defensive-impossible-null.md +157 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/defensive-missing-real.md +157 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-generic-placeholders.md +120 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-over-descriptive.md +108 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-suffix-abuse.md +167 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-type-in-name.md +100 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-dependency-creep.md +114 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-premature-interface.md +136 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-single-method-class.md +161 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-useless-wrapper.md +140 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-as-any-escape.md +127 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-debug-artifacts.md +144 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-hyper-consistent.md +84 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-no-hack-scars.md +113 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-trivial-boilerplate.md +191 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-doesnt-throw.md +132 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-mirror-implementation.md +125 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-mock-everything.md +148 -0
- package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-snapshot-abuse.md +131 -0
- package/build/Assets/Blueprints/skills/int-codebase-design/SKILL.md +116 -0
- package/build/Assets/Blueprints/skills/int-codebase-design/references/DEEPENING.md +37 -0
- package/build/Assets/Blueprints/skills/int-codebase-design/references/DESIGN-IT-TWICE.md +44 -0
- package/build/Assets/Blueprints/skills/int-domain-modeling/SKILL.md +78 -0
- package/build/Assets/Blueprints/skills/int-domain-modeling/references/ADR-FORMAT.md +47 -0
- package/build/Assets/Blueprints/skills/int-domain-modeling/references/GLOSSARY-FORMAT.md +60 -0
- package/build/Assets/Blueprints/skills/int-prototype/LOGIC.md +67 -0
- package/build/Assets/Blueprints/skills/int-prototype/SKILL.md +30 -0
- package/build/Assets/Blueprints/skills/int-prototype/UI.md +112 -0
- package/build/Assets/Blueprints/skills/int-retrospective/SKILL.md +88 -0
- package/build/Assets/Blueprints/skills/int-retrospective/assets/retrospective-prompt.md +38 -0
- package/build/Assets/Blueprints/skills/int-retrospective/references/assessment.md +56 -0
- package/build/Assets/Blueprints/skills/int-subagent-driven-dev/SKILL.md +64 -0
- package/build/Assets/Blueprints/skills/int-subagent-driven-dev/assets/implementer-prompt.md +40 -0
- package/build/Assets/Blueprints/skills/int-subagent-driven-dev/assets/reviewer-prompt.md +43 -0
- package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/aggregate-review.md +7 -0
- package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/corrective-rounds.md +72 -0
- package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/example-workflow.md +25 -0
- package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/full-spec.md +72 -0
- package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/implementer-corrections.md +9 -0
- package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/review-inputs.md +26 -0
- package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/verification-evidence.md +7 -0
- package/build/Assets/Blueprints/skills/int-test-driven-development/SKILL.md +51 -0
- package/build/Assets/Blueprints/skills/int-test-driven-development/references/mocking.md +31 -0
- package/build/Assets/Blueprints/skills/int-test-driven-development/references/tests.md +68 -0
- package/build/Assets/Blueprints/skills/int-testing-agent-guidance/SKILL.md +53 -0
- package/build/Assets/Blueprints/skills/int-verification-before-completion/SKILL.md +25 -0
- package/build/Assets/Blueprints/skills/int-writing-agents/SKILL.md +66 -0
- package/build/Assets/Blueprints/skills/int-writing-for-agents/SKILL.md +86 -0
- package/build/Assets/Blueprints/skills/int-writing-instruction/SKILL.md +57 -0
- package/build/Assets/Blueprints/skills/int-writing-skills/SKILL.md +129 -0
- package/build/Assets/Blueprints/skills/int-writing-skills/assets/interactive.md +83 -0
- package/build/Assets/Blueprints/skills/int-writing-skills/assets/reference.md +32 -0
- package/build/Assets/Blueprints/skills/int-writing-skills/references/arguments.md +50 -0
- package/build/Assets/Blueprints/skills/int-writing-skills/references/interactive-skills.md +53 -0
- package/build/Assets/Blueprints/skills/int-writing-skills/references/persuasion-principles.md +220 -0
- package/build/Assets/Blueprints/skills/int-writing-skills/references/reference-skills.md +20 -0
- package/build/Assets/Blueprints/skills/int-writing-skills/references/testing-skills.md +48 -0
- package/build/Assets/Blueprints/skills/retrospective/SKILL.md +16 -0
- package/build/Assets/Blueprints/skills/setup-project/SKILL.md +66 -0
- package/build/Assets/Blueprints/skills/to-spec/SKILL.md +53 -0
- package/build/Assets/Blueprints/skills/to-tickets/SKILL.md +49 -0
- package/build/Assets/Blueprints/skills/update-tooling/SKILL.md +86 -0
- package/build/Assets/Templates/Catalog.js +12 -0
- package/build/Assets/Templates/Catalog.ts +17 -0
- package/build/Assets/Templates/project/copilot-instructions.md +17 -0
- package/build/Assets/Templates/project/domain.md +51 -0
- package/build/Assets/Templates/project/ticket-tracker.md +33 -0
- package/build/Assets/Templates/sdd/design.md +25 -0
- package/build/Assets/Templates/sdd/spec.md +29 -0
- package/build/Assets/Templates/sdd/ticket.md +15 -0
- package/build/BlueprintManagement/BlueprintManagementServiceProvider.js +15 -0
- package/build/BlueprintManagement/Catalog/BlueprintCatalog.js +238 -0
- package/build/BlueprintManagement/Commands/BlueprintCommandOptions.js +9 -0
- package/build/BlueprintManagement/Commands/ConflictPolicy.js +6 -0
- package/build/BlueprintManagement/Commands/InitCommand.js +117 -0
- package/build/BlueprintManagement/Commands/Prompts.js +66 -0
- package/build/BlueprintManagement/Commands/UpdateCommand.js +86 -0
- package/build/BlueprintManagement/Handlers/Agents/AgentHandler.js +21 -0
- package/build/BlueprintManagement/Handlers/BlueprintHandlers.js +4 -0
- package/build/BlueprintManagement/Handlers/Instructions/InstructionHandler.js +21 -0
- package/build/BlueprintManagement/Handlers/Skills/SkillHandler.js +10 -0
- package/build/BlueprintManagement/Reconciliation/BlueprintCatalogPlanner.js +87 -0
- package/build/BlueprintManagement/Reconciliation/BlueprintRemovalCoordinator.js +78 -0
- package/build/BlueprintManagement/Services/BlueprintReconciliationService.js +391 -0
- package/build/BlueprintManagement/Services/InstallationService.js +54 -0
- package/build/BlueprintManagement/Support/BlueprintFiles.js +113 -0
- package/build/BlueprintManagement/Types/Blueprint.js +1 -0
- package/build/BlueprintManagement/Types/Installation.js +1 -0
- package/build/BlueprintManagement/Types/Reconciliation.js +1 -0
- package/build/BlueprintManagement/Versioning/BlueprintVersion.js +70 -0
- package/build/BlueprintManagement/Versioning/BlueprintVersioning.js +55 -0
- package/build/Commands/Command.js +3 -0
- package/build/Commands/CommandOptions.js +12 -0
- package/build/Config/Config.js +3 -0
- package/build/Foundation/Application.js +112 -0
- package/build/Foundation/ApplicationBuilder.js +23 -0
- package/build/Foundation/Bootstrap/Bootstrapper.js +2 -0
- package/build/Foundation/Bootstrap/DiscoverGitRoot.js +8 -0
- package/build/Foundation/Bootstrap/LoadConsoleArgs.js +47 -0
- package/build/Foundation/Bootstrap/LoadProjectConfiguration.js +144 -0
- package/build/Foundation/Bootstrap/RegisterProviders.js +12 -0
- package/build/Foundation/Bootstrap/RequireProjectConfiguration.js +14 -0
- package/build/Foundation/ConsoleInvocation.js +1 -0
- package/build/Foundation/Container.js +185 -0
- package/build/Foundation/FoundationServiceProvider.js +5 -0
- package/build/Foundation/Kernel.js +30 -0
- package/build/Foundation/Service.js +23 -0
- package/build/Foundation/ServiceProvider.js +12 -0
- package/build/Git/GitServiceProvider.js +8 -0
- package/build/Git/Services/GitService.js +26 -0
- package/build/Project/ProjectServiceProvider.js +8 -0
- package/build/Project/Services/ProjectService.js +124 -0
- package/build/SubagentDrivenDevelopment/Commands/ChecksRecordCommand.js +35 -0
- package/build/SubagentDrivenDevelopment/Commands/ChecksRequireCommand.js +33 -0
- package/build/SubagentDrivenDevelopment/Commands/ClaimCommand.js +23 -0
- package/build/SubagentDrivenDevelopment/Commands/CorrectCommand.js +37 -0
- package/build/SubagentDrivenDevelopment/Commands/CreateCommand.js +26 -0
- package/build/SubagentDrivenDevelopment/Commands/FindingCreateCommand.js +39 -0
- package/build/SubagentDrivenDevelopment/Commands/FindingUpdateCommand.js +36 -0
- package/build/SubagentDrivenDevelopment/Commands/FinishCommand.js +23 -0
- package/build/SubagentDrivenDevelopment/Commands/ImplementCommand.js +23 -0
- package/build/SubagentDrivenDevelopment/Commands/NextCommand.js +21 -0
- package/build/SubagentDrivenDevelopment/Commands/ReadyCommand.js +21 -0
- package/build/SubagentDrivenDevelopment/Commands/RequirementsDecideCommand.js +33 -0
- package/build/SubagentDrivenDevelopment/Commands/ResumeCommand.js +31 -0
- package/build/SubagentDrivenDevelopment/Commands/ReviewCommand.js +55 -0
- package/build/SubagentDrivenDevelopment/Commands/ReviewRequireCommand.js +12 -0
- package/build/SubagentDrivenDevelopment/Commands/StatusCommand.js +21 -0
- package/build/SubagentDrivenDevelopment/Commands/SubmitCommand.js +59 -0
- package/build/SubagentDrivenDevelopment/Commands/TicketCommentCommand.js +24 -0
- package/build/SubagentDrivenDevelopment/Commands/TicketCreateCommand.js +25 -0
- package/build/SubagentDrivenDevelopment/Commands/TicketUpdateCommand.js +31 -0
- package/build/SubagentDrivenDevelopment/Commands/groups.js +36 -0
- package/build/SubagentDrivenDevelopment/Evidence/GitEvidence.js +449 -0
- package/build/SubagentDrivenDevelopment/Evidence/ReportEvidence.js +80 -0
- package/build/SubagentDrivenDevelopment/Models/Assignments.js +1 -0
- package/build/SubagentDrivenDevelopment/Models/Evidence.js +1 -0
- package/build/SubagentDrivenDevelopment/Models/Findings.js +1 -0
- package/build/SubagentDrivenDevelopment/Models/Options.js +1 -0
- package/build/SubagentDrivenDevelopment/Models/Workflow.js +16 -0
- package/build/SubagentDrivenDevelopment/Persistence/AssignmentSchema.js +155 -0
- package/build/SubagentDrivenDevelopment/Persistence/CheckSchema.js +71 -0
- package/build/SubagentDrivenDevelopment/Persistence/FeatureWorkspace.js +76 -0
- package/build/SubagentDrivenDevelopment/Persistence/FileSystem.js +28 -0
- package/build/SubagentDrivenDevelopment/Persistence/FindingSchema.js +81 -0
- package/build/SubagentDrivenDevelopment/Persistence/LedgerRepository.js +322 -0
- package/build/SubagentDrivenDevelopment/Persistence/PathSafety.js +62 -0
- package/build/SubagentDrivenDevelopment/Persistence/ProgressSchema.js +95 -0
- package/build/SubagentDrivenDevelopment/Persistence/ReadinessReader.js +40 -0
- package/build/SubagentDrivenDevelopment/Persistence/ReviewSchema.js +68 -0
- package/build/SubagentDrivenDevelopment/Persistence/TicketRepository.js +147 -0
- package/build/SubagentDrivenDevelopment/Persistence/WorkspaceTransaction.js +282 -0
- package/build/SubagentDrivenDevelopment/Policies/AcceptancePolicy.js +100 -0
- package/build/SubagentDrivenDevelopment/Policies/AssignmentPolicy.js +36 -0
- package/build/SubagentDrivenDevelopment/Policies/CheckPolicy.js +93 -0
- package/build/SubagentDrivenDevelopment/Policies/FindingPolicy.js +53 -0
- package/build/SubagentDrivenDevelopment/Policies/InputValidation.js +36 -0
- package/build/SubagentDrivenDevelopment/Policies/ReadinessPolicy.js +26 -0
- package/build/SubagentDrivenDevelopment/Policies/RequiredReviewers.js +12 -0
- package/build/SubagentDrivenDevelopment/Policies/ReviewPolicy.js +87 -0
- package/build/SubagentDrivenDevelopment/Policies/WorkflowPolicy.js +85 -0
- package/build/SubagentDrivenDevelopment/Services/AssignmentService.js +269 -0
- package/build/SubagentDrivenDevelopment/Services/ChecksService.js +319 -0
- package/build/SubagentDrivenDevelopment/Services/CorrectionService.js +273 -0
- package/build/SubagentDrivenDevelopment/Services/DeliveryCorrectionService.js +253 -0
- package/build/SubagentDrivenDevelopment/Services/DeliveryFindingService.js +302 -0
- package/build/SubagentDrivenDevelopment/Services/DeliveryService.js +160 -0
- package/build/SubagentDrivenDevelopment/Services/DeliverySubmissionService.js +273 -0
- package/build/SubagentDrivenDevelopment/Services/FeatureCreationService.js +138 -0
- package/build/SubagentDrivenDevelopment/Services/FeatureInspectionService.js +399 -0
- package/build/SubagentDrivenDevelopment/Services/FindingService.js +392 -0
- package/build/SubagentDrivenDevelopment/Services/IntegrationService.js +258 -0
- package/build/SubagentDrivenDevelopment/Services/ReadinessService.js +21 -0
- package/build/SubagentDrivenDevelopment/Services/RequirementsService.js +216 -0
- package/build/SubagentDrivenDevelopment/Services/ReviewService.js +471 -0
- package/build/SubagentDrivenDevelopment/Services/SubmissionService.js +299 -0
- package/build/SubagentDrivenDevelopment/Services/TicketService.js +186 -0
- package/build/SubagentDrivenDevelopment/SubagentDrivenDevelopmentServiceProvider.js +113 -0
- package/build/Support/Records.js +5 -0
- package/build/Support/Repository.js +139 -0
- package/build/Support/Stacks.js +10 -0
- package/build/TemplateRendering/Computed/ComputedContextService.js +13 -0
- package/build/TemplateRendering/Computed/ComputedVariableProvider.js +6 -0
- package/build/TemplateRendering/Computed/ComputedVariableProviders.js +6 -0
- package/build/TemplateRendering/Computed/Providers/InternalSkillDescriptionProvider.js +6 -0
- package/build/TemplateRendering/Computed/Providers/SpindlePackageManagerProvider.js +11 -0
- package/build/TemplateRendering/Computed/ResolveRenderContext.js +18 -0
- package/build/TemplateRendering/Rendering/RenderText.js +43 -0
- package/build/TemplateRendering/TemplateRenderingServiceProvider.js +8 -0
- package/build/Tooling/Commands/ToolingAddCommand.js +29 -0
- package/build/Tooling/Commands/ToolingListCommand.js +19 -0
- package/build/Tooling/Commands/ToolingRemoveCommand.js +19 -0
- package/build/Tooling/Commands/ToolingUpdateCommand.js +30 -0
- package/build/Tooling/Commands/groups.js +4 -0
- package/build/Tooling/Models/ToolingEntry.js +1 -0
- package/build/Tooling/Services/ToolingService.js +278 -0
- package/build/Tooling/ToolingServiceProvider.js +15 -0
- package/build/cli.js +36 -0
- package/build/index.js +31 -0
- package/package.json +54 -3
package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-generic-placeholders.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Generic Placeholder Names
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: "Reduces every variable to 'a thing' — kills the most powerful form of self-documentation"
|
|
5
|
+
tags: naming, generic-names, ai-fingerprint
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Generic Placeholder Names
|
|
9
|
+
|
|
10
|
+
**Impact: CRITICAL (Reduces every variable to 'a thing' — kills the most powerful form of self-documentation)**
|
|
11
|
+
|
|
12
|
+
`data`, `result`, `info`, `temp`, `value`, `item`, `helper`, `manager`, `utils` — these are the names a model picks when it doesn't know what the value represents in the domain. They pass linters, type checks, every metric. They cost the reader real attention to track ("which `data` is this?").
|
|
13
|
+
|
|
14
|
+
Names are the cheapest, highest-leverage form of self-documentation in any codebase. A model that names every intermediate variable `data` or `result` is signalling it didn't think about the domain.
|
|
15
|
+
|
|
16
|
+
## Incorrect
|
|
17
|
+
|
|
18
|
+
```php
|
|
19
|
+
// ❌ Every variable is generic
|
|
20
|
+
|
|
21
|
+
public function exportUsers(): Collection
|
|
22
|
+
{
|
|
23
|
+
$data = User::all();
|
|
24
|
+
|
|
25
|
+
$result = [];
|
|
26
|
+
foreach ($data as $item) {
|
|
27
|
+
$info = [
|
|
28
|
+
'name' => $item->name,
|
|
29
|
+
'email' => $item->email,
|
|
30
|
+
];
|
|
31
|
+
$result[] = $info;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return collect($result);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// ❌ Same pattern
|
|
40
|
+
async function fetchOrders(userId: string) {
|
|
41
|
+
const data = await api.getOrders(userId);
|
|
42
|
+
const result = data.map((item) => {
|
|
43
|
+
const info = {
|
|
44
|
+
id: item.id,
|
|
45
|
+
total: item.total,
|
|
46
|
+
};
|
|
47
|
+
return info;
|
|
48
|
+
});
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Why it's slop:**
|
|
54
|
+
|
|
55
|
+
- Every variable is "a thing"; reader has no anchor to the domain
|
|
56
|
+
- `$item` inside a loop over `$data` is doubly opaque — what kind of item? what kind of data?
|
|
57
|
+
- A reader hitting line 50 of this file can't tell what `$result` holds without tracing the whole function
|
|
58
|
+
- A human author who lived with this code would use domain words
|
|
59
|
+
|
|
60
|
+
## Correct
|
|
61
|
+
|
|
62
|
+
```php
|
|
63
|
+
// ✅ Names tell you what the values are in the domain
|
|
64
|
+
|
|
65
|
+
public function exportUsers(): Collection
|
|
66
|
+
{
|
|
67
|
+
return User::all()
|
|
68
|
+
->map(fn (User $user) => [
|
|
69
|
+
'name' => $user->name,
|
|
70
|
+
'email' => $user->email,
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// ✅ Domain words everywhere
|
|
77
|
+
async function fetchOrders(userId: string): Promise<OrderSummary[]> {
|
|
78
|
+
const orders = await api.getOrders(userId);
|
|
79
|
+
return orders.map((order) => ({
|
|
80
|
+
id: order.id,
|
|
81
|
+
total: order.total,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Why it reads human:**
|
|
87
|
+
|
|
88
|
+
- A reader on any line knows the domain object in scope
|
|
89
|
+
- Domain names compose — `orders.map(order => …)` reads as a sentence
|
|
90
|
+
- The original `$data → $item → $info → $result` chain collapses into one expression because the names made the intermediate variables unnecessary
|
|
91
|
+
|
|
92
|
+
## When generic names ARE OK
|
|
93
|
+
|
|
94
|
+
A handful of names are conventional, short-scope, and fine:
|
|
95
|
+
|
|
96
|
+
- **`i`, `j`, `k`** — loop indices in a 3-line `for`
|
|
97
|
+
- **`x`, `y`, `z`** — math/geometry (coordinates)
|
|
98
|
+
- **`_`** — explicit "ignored value"
|
|
99
|
+
- **`acc`** — accumulator in a `reduce` callback
|
|
100
|
+
- **`prev`, `next`** — in middleware / chained handlers where they're language conventions
|
|
101
|
+
- **`req`, `res`** — Express handlers (don't fight a framework convention)
|
|
102
|
+
|
|
103
|
+
If the value flows through 5+ lines or escapes the immediate function, use a domain name.
|
|
104
|
+
|
|
105
|
+
## Detection
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Bare local-variable declarations using generic names (PHP)
|
|
109
|
+
grep -rEn '\$(data|result|info|temp|item|helper|value)\b' --include='*.php' app/ | wc -l
|
|
110
|
+
|
|
111
|
+
# TS/JS — generic const/let
|
|
112
|
+
grep -rEn '\b(const|let)\s+(data|result|info|temp|helper|value|item)\b' --include='*.ts' --include='*.tsx' --include='*.js' src/ | wc -l
|
|
113
|
+
|
|
114
|
+
# Density signal: files with > 10 generic-name hits
|
|
115
|
+
# (in a 200-line file, 10+ generic names is suspicious)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
There's no fully automatic detector — judgment is required. The signal is **density**: a few are fine, a thicket is a slop fingerprint.
|
|
119
|
+
|
|
120
|
+
Reference: [Clean Code (Robert C. Martin) — Chapter 2: Meaningful Names](https://www.oreilly.com/library/view/clean-code/9780136083238/) · Internal: [`naming-over-descriptive`](naming-over-descriptive.md)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Over-Descriptive Run-On Names
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: "Inflated identifiers that read like English instead of code"
|
|
5
|
+
tags: naming, verbose, ai-fingerprint
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Over-Descriptive Run-On Names
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (Inflated identifiers that read like English instead of code)**
|
|
11
|
+
|
|
12
|
+
The opposite failure mode of generic names: instead of `user`, the AI produces `theUserWhoIsCurrentlyLoggedIn`. Instead of `total`, `calculateTotalAmountFromItemsList`. The identifier becomes a sentence. Real engineers know that _context_ (function scope, parameter types, surrounding code) carries half the meaning — you don't need to encode all of it in every variable name.
|
|
13
|
+
|
|
14
|
+
A name should be **as long as needed and no longer**. Once context establishes "we're inside `processOrder`", calling the order variable `order` is fine. Calling it `theOrderThatIsCurrentlyBeingProcessed` is slop.
|
|
15
|
+
|
|
16
|
+
## Incorrect
|
|
17
|
+
|
|
18
|
+
```php
|
|
19
|
+
// ❌ Identifiers read as English
|
|
20
|
+
|
|
21
|
+
public function processOrderForCheckout(
|
|
22
|
+
OrderThatIsCurrentlyBeingProcessed $theOrderToProcess
|
|
23
|
+
): ProcessingResultObjectContainingStatus {
|
|
24
|
+
$theCurrentlyLoggedInUserWhoIsCheckingOut = $theOrderToProcess->getUserWhoPlacedTheOrder();
|
|
25
|
+
$theTotalAmountThatNeedsToBeCharged = $this->calculateTotalAmountFromItemsListInTheOrder($theOrderToProcess);
|
|
26
|
+
|
|
27
|
+
return new ProcessingResultObjectContainingStatus(
|
|
28
|
+
status: 'success',
|
|
29
|
+
amountThatWasCharged: $theTotalAmountThatNeedsToBeCharged,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// ❌ Same in TS
|
|
36
|
+
async function fetchTheUserDataObjectForTheUserWithTheGivenId(
|
|
37
|
+
theIdOfTheUserToFetch: string,
|
|
38
|
+
): Promise<TheUserDataResponseObject> {
|
|
39
|
+
const theResponseFromTheAPI = await api.getUser(theIdOfTheUserToFetch);
|
|
40
|
+
return theResponseFromTheAPI;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Why it's slop:**
|
|
45
|
+
|
|
46
|
+
- Function signatures span multiple lines because identifiers eat the line budget
|
|
47
|
+
- Reader has to scan past redundant words ("theOrderToProcess" — yes, it's the parameter, of course we're processing it)
|
|
48
|
+
- "The" prefix is meaningless — what else would it be?
|
|
49
|
+
- Inflates token count without conveying anything new
|
|
50
|
+
|
|
51
|
+
## Correct
|
|
52
|
+
|
|
53
|
+
```php
|
|
54
|
+
// ✅ Tight; context carries the obvious
|
|
55
|
+
|
|
56
|
+
public function checkout(Order $order): ProcessingResult
|
|
57
|
+
{
|
|
58
|
+
$user = $order->user;
|
|
59
|
+
$total = $this->totalFor($order);
|
|
60
|
+
|
|
61
|
+
return new ProcessingResult(status: 'success', charged: $total);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// ✅ Tight TS
|
|
67
|
+
async function fetchUser(id: string): Promise<User> {
|
|
68
|
+
return api.getUser(id);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Why it reads human:**
|
|
73
|
+
|
|
74
|
+
- The function is named `checkout`; obviously the parameter is the order being checked out — no need to spell that out
|
|
75
|
+
- Function fits on one line; reader takes it in at a glance
|
|
76
|
+
- Domain words (`Order`, `User`, `ProcessingResult`) replace English sentences
|
|
77
|
+
|
|
78
|
+
## Guidelines
|
|
79
|
+
|
|
80
|
+
| Scope | Reasonable length |
|
|
81
|
+
| ------------------------ | ------------------------------------------------------------------- |
|
|
82
|
+
| Loop index | 1 char (`i`) |
|
|
83
|
+
| Block-local (< 10 lines) | 1 word (`user`, `total`) |
|
|
84
|
+
| Function-local | 1–2 words (`pricedItems`, `taxedTotal`) |
|
|
85
|
+
| Public API parameter | 2–3 words when domain requires (`stripeChargeId`, `webhookPayload`) |
|
|
86
|
+
| Constants / config keys | Domain-precise (`MAX_STRIPE_AMOUNT_CENTS`) — those CAN be long |
|
|
87
|
+
| Class names | 1–3 words (`OrderService`, `RefundCalculator`) |
|
|
88
|
+
|
|
89
|
+
The rule of thumb: **if you can drop a word and meaning survives, drop it.**
|
|
90
|
+
|
|
91
|
+
## Detection
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Identifiers with 4+ camelCase words (heuristic; tune to taste).
|
|
95
|
+
# Note: grep -E (ERE) does NOT support (?:...) non-capturing groups; use a capturing group.
|
|
96
|
+
grep -rEn '[a-z]+([A-Z][a-z]+){3,}' --include='*.ts' --include='*.tsx' --include='*.js' src/ \
|
|
97
|
+
| head -30
|
|
98
|
+
|
|
99
|
+
# PHP equivalent (variables / method names)
|
|
100
|
+
grep -rEn '\$[a-z][a-zA-Z]{20,}' --include='*.php' app/ | head -30
|
|
101
|
+
grep -rEn 'function [a-z][a-zA-Z]{30,}\(' --include='*.php' app/ | head -30
|
|
102
|
+
|
|
103
|
+
# Names starting with "the" — a strong AI tell
|
|
104
|
+
grep -rEnH '\b(theUser|theOrder|theData|theResult|theItem|theCurrent)' \
|
|
105
|
+
--include='*.php' --include='*.ts' --include='*.tsx' app/ src/
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Reference: [Clean Code — Chapter 2](https://www.oreilly.com/library/view/clean-code/9780136083238/) · Internal: [`naming-generic-placeholders`](naming-generic-placeholders.md) (the opposite failure mode)
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Suffix Abuse — *Helper, *Manager, *Util, *Wrapper, *Processor
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: "Catch-all suffixes signal undecided design and pad code with empty abstractions"
|
|
5
|
+
tags: naming, suffixes, ai-fingerprint, abstractions
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Suffix Abuse — *Helper,*Manager,*Util,*Wrapper,*Processor
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (Catch-all suffixes signal undecided design and pad code with empty abstractions)**
|
|
11
|
+
|
|
12
|
+
`UserHelper`, `OrderManager`, `DataUtil`, `RequestWrapper`, `PaymentProcessor` — these suffixes are the AI's go-to when it doesn't know what to call something. The suffix is non-information: every class is in some sense a "manager" or "helper" or "util". The name describes the noun-form of "code I had to put somewhere".
|
|
13
|
+
|
|
14
|
+
When a model generates these, it's because:
|
|
15
|
+
|
|
16
|
+
- The "Helper" class is doing things that belong on the model
|
|
17
|
+
- The "Manager" is a procedural blob masquerading as a class
|
|
18
|
+
- The "Util" is a kitchen sink that should be split or absorbed into domain types
|
|
19
|
+
|
|
20
|
+
Real domain code uses domain names: `Pricing`, `RefundPolicy`, `WebhookVerifier` — not `PricingHelper`, `RefundManager`, `WebhookProcessor`.
|
|
21
|
+
|
|
22
|
+
## Incorrect
|
|
23
|
+
|
|
24
|
+
```php
|
|
25
|
+
// ❌ Suffix abuse — every concern wrapped in *Helper or *Manager
|
|
26
|
+
|
|
27
|
+
class UserHelper
|
|
28
|
+
{
|
|
29
|
+
public function formatName(User $user): string {
|
|
30
|
+
return $user->firstName . ' ' . $user->lastName;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class OrderManager
|
|
35
|
+
{
|
|
36
|
+
public function processOrder(Order $order): void {
|
|
37
|
+
// ...
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class PaymentProcessor
|
|
42
|
+
{
|
|
43
|
+
public function processPayment(Order $order, string $token): Charge {
|
|
44
|
+
// ...
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class DataUtil
|
|
49
|
+
{
|
|
50
|
+
public static function arrayToCsv(array $rows): string { /* ... */ }
|
|
51
|
+
public static function csvToArray(string $csv): array { /* ... */ }
|
|
52
|
+
public static function snakeToCamel(string $s): string { /* ... */ }
|
|
53
|
+
public static function camelToSnake(string $s): string { /* ... */ }
|
|
54
|
+
// …grows forever
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// ❌ Same TS pattern
|
|
60
|
+
class StringHelper {
|
|
61
|
+
static capitalize(s: string): string {
|
|
62
|
+
/* ... */
|
|
63
|
+
}
|
|
64
|
+
static slugify(s: string): string {
|
|
65
|
+
/* ... */
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
class ResponseWrapper {
|
|
70
|
+
constructor(private res: ApiResponse) {}
|
|
71
|
+
getData() {
|
|
72
|
+
return this.res.data;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Why it's slop:**
|
|
78
|
+
|
|
79
|
+
- `UserHelper.formatName(user)` could just be `user.fullName` on the User model
|
|
80
|
+
- `OrderManager.processOrder(order)` is two ways of saying the same thing
|
|
81
|
+
- `DataUtil` is a kitchen sink — every utility ends up here, none have a clear home
|
|
82
|
+
- The suffix doesn't add information; it admits the author didn't decide what the class is
|
|
83
|
+
|
|
84
|
+
## Correct
|
|
85
|
+
|
|
86
|
+
```php
|
|
87
|
+
// ✅ Behavior lives on the domain type; helpers split by concern
|
|
88
|
+
|
|
89
|
+
class User extends Model
|
|
90
|
+
{
|
|
91
|
+
public function getFullNameAttribute(): string
|
|
92
|
+
{
|
|
93
|
+
return "{$this->firstName} {$this->lastName}";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Action class — replaces "OrderManager.processOrder"
|
|
98
|
+
final class PlaceOrder
|
|
99
|
+
{
|
|
100
|
+
public function __invoke(Order $order, PaymentIntent $payment): void { /* ... */ }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Domain class with a clear single responsibility — replaces "PaymentProcessor"
|
|
104
|
+
final class StripePaymentGateway implements PaymentGateway
|
|
105
|
+
{
|
|
106
|
+
public function charge(Money $amount, string $token): Charge { /* ... */ }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// CSV becomes its own type — replaces the kitchen-sink DataUtil
|
|
110
|
+
final class CsvExporter
|
|
111
|
+
{
|
|
112
|
+
public function export(iterable $rows, array $headers): string { /* ... */ }
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// ✅ Methods on the domain type, or named domain functions
|
|
118
|
+
class User {
|
|
119
|
+
get fullName(): string {
|
|
120
|
+
return `${this.firstName} ${this.lastName}`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Free function — capitalize doesn't need a class wrapper
|
|
125
|
+
export function capitalize(s: string): string {
|
|
126
|
+
/* ... */
|
|
127
|
+
}
|
|
128
|
+
export function slugify(s: string): string {
|
|
129
|
+
/* ... */
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Why it reads human:**
|
|
134
|
+
|
|
135
|
+
- Each class has a specific domain responsibility (CsvExporter exports CSVs; PlaceOrder places orders)
|
|
136
|
+
- Behavior-on-data is on the type that holds the data (`user.fullName`)
|
|
137
|
+
- Free functions stand on their own when there's no state to wrap
|
|
138
|
+
|
|
139
|
+
## When suffixes ARE okay
|
|
140
|
+
|
|
141
|
+
A handful of suffixes carry real meaning in their conventions:
|
|
142
|
+
|
|
143
|
+
- **`*Repository`** — DDD-style data access (specific contract)
|
|
144
|
+
- **`*Service`** — used sparingly for orchestration that doesn't fit on a domain type
|
|
145
|
+
- **`*Controller`** — HTTP entry point (Laravel/Express convention)
|
|
146
|
+
- **`*Middleware`** — request pipeline (Express/Laravel convention)
|
|
147
|
+
- **`*Gateway`** — external integration boundary (Stripe, AWS)
|
|
148
|
+
- **`*Listener`** / `*Observer` — event-handler conventions (Laravel)
|
|
149
|
+
|
|
150
|
+
`*Helper`, `*Manager`, `*Util`, `*Wrapper`, `*Processor` are the suspect tier.
|
|
151
|
+
|
|
152
|
+
## Detection
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Class declarations with suspect suffixes
|
|
156
|
+
grep -rEn 'class\s+[A-Z][a-zA-Z]*(Helper|Manager|Util|Utils|Wrapper|Processor|Handler)\b' \
|
|
157
|
+
--include='*.php' --include='*.ts' --include='*.tsx' app/ src/
|
|
158
|
+
|
|
159
|
+
# Count by suffix to size the problem
|
|
160
|
+
grep -rE 'class\s+[A-Z][a-zA-Z]*' --include='*.php' --include='*.ts' app/ src/ \
|
|
161
|
+
| grep -oE '(Helper|Manager|Util|Utils|Wrapper|Processor)\b' \
|
|
162
|
+
| sort | uniq -c | sort -rn
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
A repo with **5+ `*Helper`** or **3+ `*Manager`** classes is almost certainly leaking domain logic into catch-all classes. Refactor by asking "what would I call this if I couldn't use the suffix?"
|
|
166
|
+
|
|
167
|
+
Reference: [Clean Code — Chapter 2: Meaningful Names](https://www.oreilly.com/library/view/clean-code/9780136083238/) · Internal: [`over-eng-single-method-class`](over-eng-single-method-class.md)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Type Encoded in Variable Names
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: "Embeds type in the identifier when the type system already does it; ages badly with refactors"
|
|
5
|
+
tags: naming, hungarian, type-in-name, ai-fingerprint
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Type Encoded in Variable Names
|
|
9
|
+
|
|
10
|
+
**Impact: MEDIUM (Embeds type in the identifier when the type system already does it; ages badly with refactors)**
|
|
11
|
+
|
|
12
|
+
`userObject`, `resultArray`, `stringData`, `listOfItems`, `userIdString`, `dataMap` — these names encode the _type_ of the value, redundantly with the actual type declaration. Both PHP 8+ and TypeScript have rich type systems that show the type in the signature. The name should encode what the value **means**, not what its shape is.
|
|
13
|
+
|
|
14
|
+
The pattern is a tell because it suggests the author optimised for "obvious from any one line" rather than "obvious in context". A human author trusts the reader to look at the signature.
|
|
15
|
+
|
|
16
|
+
## Incorrect
|
|
17
|
+
|
|
18
|
+
```php
|
|
19
|
+
// ❌ Type-in-name everywhere
|
|
20
|
+
|
|
21
|
+
public function processItemsList(array $itemsArray, int $userIdInteger): array
|
|
22
|
+
{
|
|
23
|
+
$resultArray = [];
|
|
24
|
+
foreach ($itemsArray as $itemObject) {
|
|
25
|
+
$priceFloat = $itemObject->priceFloat;
|
|
26
|
+
$resultArray[] = $priceFloat;
|
|
27
|
+
}
|
|
28
|
+
return $resultArray;
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// ❌ Same in TS
|
|
34
|
+
function getUserDataObject(userIdString: string): UserDataResponseObject {
|
|
35
|
+
const responseObject = await api.fetch(userIdString);
|
|
36
|
+
const dataArray = responseObject.dataArray;
|
|
37
|
+
const userObjectList = dataArray.map((itemObject: UserObject) => itemObject);
|
|
38
|
+
return userObjectList;
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Why it's slop:**
|
|
43
|
+
|
|
44
|
+
- The type is declared on the parameter and visible in the signature
|
|
45
|
+
- Renaming `$itemsArray` to a `Collection` requires updating every variable reference
|
|
46
|
+
- The name `$itemObject` is redundant — the static type says it's a `User`
|
|
47
|
+
- Pattern is a clear "tutorial-blog" signature; teammates don't write like this
|
|
48
|
+
|
|
49
|
+
## Correct
|
|
50
|
+
|
|
51
|
+
```php
|
|
52
|
+
// ✅ Names encode meaning, not shape
|
|
53
|
+
|
|
54
|
+
public function totalsFor(array $items, int $userId): array
|
|
55
|
+
{
|
|
56
|
+
return array_map(fn (Item $item) => $item->price, $items);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// ✅ Domain words, types in the signature
|
|
62
|
+
async function fetchUser(userId: string): Promise<User> {
|
|
63
|
+
const { data } = await api.fetch(userId);
|
|
64
|
+
return data;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Why it reads human:**
|
|
69
|
+
|
|
70
|
+
- Each name describes the role (`items`, `userId`, `total`) not the shape
|
|
71
|
+
- Type info is in the type system, where refactors update it automatically
|
|
72
|
+
- If `items` becomes a `Collection<Item>`, the variable name still reads correctly
|
|
73
|
+
|
|
74
|
+
## When type-in-name IS okay
|
|
75
|
+
|
|
76
|
+
Conventions where the type is part of the meaning:
|
|
77
|
+
|
|
78
|
+
- **`*Id`** suffix — `userId` (distinguishes from `user` which is the whole object) ✓
|
|
79
|
+
- **`*Count`** suffix — `userCount` ✓
|
|
80
|
+
- **`is*`** / **`has*`** / **`can*`** booleans — `isPaid`, `hasShipping`, `canEdit` ✓
|
|
81
|
+
- **Maps with two-type names** — `usersByEmail` (carries both types in one word) ✓
|
|
82
|
+
- **DTO classes** — `UserResponse`, `OrderPayload` (clarifies "this is the wire form, not the domain model") ✓
|
|
83
|
+
|
|
84
|
+
The suspect patterns are `*Object`, `*Array`, `*List`, `*Map`, `*String`, `*Integer`, `*Float` on local variables.
|
|
85
|
+
|
|
86
|
+
## Detection
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Suspect type-suffix names on variables (PHP)
|
|
90
|
+
grep -rEn '\$[a-z][a-zA-Z]*?(Object|Array|List|Map|String|Integer|Float|Boolean|Bool)\b' \
|
|
91
|
+
--include='*.php' app/
|
|
92
|
+
|
|
93
|
+
# TS / JS
|
|
94
|
+
grep -rEn '\b(const|let)\s+[a-z][a-zA-Z]*?(Object|Array|List|Map|String|Boolean)\b' \
|
|
95
|
+
--include='*.ts' --include='*.tsx' --include='*.js' --include='*.jsx' src/
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
A few hits are fine (especially in test data setups). **Density across multiple files** is the signal — a project with 30+ `*Array` / `*Object` variables almost certainly came from a model.
|
|
99
|
+
|
|
100
|
+
Reference: [Clean Code — Chapter 2](https://www.oreilly.com/library/view/clean-code/9780136083238/) · [Joel Spolsky — Making Wrong Code Look Wrong](https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/) (the "useful Hungarian" original — different from this rule's anti-pattern) · Internal: [`naming-over-descriptive`](naming-over-descriptive.md)
|
package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-dependency-creep.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Dependency Creep — New Library When Existing One Suffices
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: "Inflates bundle/install size, adds CVE surface, signals model picked training-data favourites"
|
|
5
|
+
tags: over-engineering, dependencies, dependency-creep, ai-fingerprint
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Dependency Creep — New Library When Existing One Suffices
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (Inflates bundle/install size, adds CVE surface, signals model picked training-data favourites)**
|
|
11
|
+
|
|
12
|
+
AI tends to introduce a new dependency whenever a problem matches a library it has seen in training, even when the project already includes a dependency that solves the same problem. Example signals:
|
|
13
|
+
|
|
14
|
+
- Adding `date-fns` to a project that already uses `dayjs`
|
|
15
|
+
- Adding `axios` to a project that already imports `fetch` everywhere
|
|
16
|
+
- Adding `lodash` to a project that already has `lodash-es` (or uses native ES methods)
|
|
17
|
+
- Adding `uuid` when the project already uses `nanoid`
|
|
18
|
+
- Adding `winston` when the app uses `pino`
|
|
19
|
+
- Adding `joi` / `yup` to a TS project that already uses `zod`
|
|
20
|
+
- Adding `bcrypt` (the npm package) in a Laravel project where `Hash::make` handles it
|
|
21
|
+
- Adding `moment` in a project that just removed `moment` last quarter
|
|
22
|
+
|
|
23
|
+
Each new dependency:
|
|
24
|
+
|
|
25
|
+
- Adds to install / bundle size
|
|
26
|
+
- Adds a new CVE surface (every `npm audit` lights up)
|
|
27
|
+
- Forces the team to maintain TWO libraries for the same concern, indefinitely
|
|
28
|
+
- Drifts as the LLM picks whichever was popular in the training-data slice it sampled from
|
|
29
|
+
|
|
30
|
+
## Incorrect
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
// ❌ package.json — two libs doing the same job
|
|
34
|
+
{
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"dayjs": "^1.11.10", // already in use
|
|
37
|
+
"date-fns": "^3.0.0", // ADDED — same purpose
|
|
38
|
+
"axios": "^1.6.0", // ADDED — but app uses fetch everywhere
|
|
39
|
+
"lodash": "^4.17.21", // ADDED — lodash-es already present
|
|
40
|
+
"lodash-es": "^4.17.21"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```php
|
|
46
|
+
// ❌ composer.json — two HTTP clients
|
|
47
|
+
{
|
|
48
|
+
"require": {
|
|
49
|
+
"guzzlehttp/guzzle": "^7.8",
|
|
50
|
+
"symfony/http-client": "^7.0" // ADDED — same purpose
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Why it's slop:**
|
|
56
|
+
|
|
57
|
+
- Both libraries get pulled into every install
|
|
58
|
+
- New `dayjs` code still gets written alongside the new `date-fns` code — drift forever
|
|
59
|
+
- Two CVE feeds to track
|
|
60
|
+
- Reviewer didn't notice because each PR seems reasonable in isolation
|
|
61
|
+
|
|
62
|
+
## Correct
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# ✅ Use the existing dep; reject the PR adding the new one with a note
|
|
66
|
+
|
|
67
|
+
# Before merging a PR that adds a new dependency, ask:
|
|
68
|
+
# 1. Does an existing dependency in package.json/composer.json already do this?
|
|
69
|
+
# 2. Does a native browser/Node/PHP API already do this?
|
|
70
|
+
# 3. Is the new dep > 50KB gzipped (or > 1MB unpacked) for a single function call?
|
|
71
|
+
# 4. Does it have CVEs / abandoned upstream?
|
|
72
|
+
#
|
|
73
|
+
# If any answer is "yes" or "maybe", reject and use the existing tool.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Common collisions and the canonical choice:
|
|
77
|
+
|
|
78
|
+
| Concern | Pick one |
|
|
79
|
+
| ------------------------- | ---------------------------------------------------------------------------------------- |
|
|
80
|
+
| Date / time | `dayjs` OR `date-fns` (not both) |
|
|
81
|
+
| HTTP client | `fetch` OR `axios` (not both) |
|
|
82
|
+
| UUID | `uuid` OR `nanoid` (not both) |
|
|
83
|
+
| Schema validation (TS) | `zod` (preferred) — reject `joi`/`yup`/`ajv` additions |
|
|
84
|
+
| HTTP client (PHP) | `guzzlehttp/guzzle` (Laravel default) — reject `symfony/http-client` unless project-wide |
|
|
85
|
+
| Logging | `monolog` (Laravel default) for PHP; pick one of `pino`/`winston` for Node |
|
|
86
|
+
| Form validation (Laravel) | Built-in `FormRequest` — reject standalone validation libs |
|
|
87
|
+
|
|
88
|
+
## Detection
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Node: dependency-list audit
|
|
92
|
+
node -e "
|
|
93
|
+
const pkg = require('./package.json');
|
|
94
|
+
const all = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
95
|
+
const dupes = [
|
|
96
|
+
['dayjs', 'date-fns', 'moment', 'luxon'],
|
|
97
|
+
['axios', 'node-fetch', 'got', 'superagent'],
|
|
98
|
+
['uuid', 'nanoid', 'cuid'],
|
|
99
|
+
['lodash', 'lodash-es', 'ramda'],
|
|
100
|
+
['joi', 'yup', 'zod', 'ajv'],
|
|
101
|
+
];
|
|
102
|
+
for (const group of dupes) {
|
|
103
|
+
const found = group.filter(p => all[p]);
|
|
104
|
+
if (found.length > 1) console.log('OVERLAP:', found.join(', '));
|
|
105
|
+
}
|
|
106
|
+
"
|
|
107
|
+
|
|
108
|
+
# PHP: check for HTTP-client overlap
|
|
109
|
+
grep -E '\"(guzzlehttp/guzzle|symfony/http-client|kriswallsmith/buzz)\"' composer.json
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
When a new dep lands in a PR, the PR author should justify why the existing options don't fit. "AI suggested this" is not a justification.
|
|
113
|
+
|
|
114
|
+
Reference: [`technical-debt`'s `deps-unused-deps`](../technical-debt/rules/deps-unused-deps.md) (the broader audit) · [BundlePhobia](https://bundlephobia.com/) (size impact for npm packages)
|