@wazir-dev/cli 1.0.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.
- package/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,1118 @@
|
|
|
1
|
+
# Naming & Abstraction Anti-Patterns
|
|
2
|
+
|
|
3
|
+
> **Domain:** Code
|
|
4
|
+
> **Anti-patterns covered:** 20
|
|
5
|
+
> **Highest severity:** High
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Introduction
|
|
10
|
+
|
|
11
|
+
Naming is the most pervasive act in software development. Every variable, function, class, module, and abstraction boundary carries a name, and that name shapes every reader's mental model forever after. Bad names are not merely aesthetic failures — they are correctness failures. A function named `processData` that silently mutates global state, a class named `Utils` that accumulates 3,000 lines over two years, an abstraction named `IRepository` that leaks SQL dialect details: each of these is a defect that static analysis cannot catch but that costs teams real hours every sprint.
|
|
12
|
+
|
|
13
|
+
Abstraction anti-patterns compound naming problems. An abstraction exists to let the reader stop thinking about one level of detail. When the abstraction is premature, wrong, or inverted, it forces the reader to hold *more* levels simultaneously, not fewer. The result is cognitive overhead that compounds with codebase growth.
|
|
14
|
+
|
|
15
|
+
This module catalogues 20 concrete anti-patterns across naming and abstraction, with real-world evidence, machine-applicable detection rules, and before/after fixes.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## AP-1: Meaningless Names
|
|
20
|
+
|
|
21
|
+
**Also known as:** Noise words, placeholder names left in production, data/info/temp variables
|
|
22
|
+
**Frequency:** Very Common
|
|
23
|
+
**Severity:** High
|
|
24
|
+
**Detection difficulty:** Easy
|
|
25
|
+
|
|
26
|
+
**What it looks like:**
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
def process(data, info, temp):
|
|
30
|
+
x = data["val"]
|
|
31
|
+
y = info.get("thing")
|
|
32
|
+
result2 = x + y
|
|
33
|
+
return result2
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Why developers do it:**
|
|
37
|
+
Placeholder names are created during exploratory coding and never replaced. Developers are in flow state and treat naming as something to "fix later." Under deadline pressure, "later" never arrives.
|
|
38
|
+
|
|
39
|
+
**What goes wrong:**
|
|
40
|
+
Every future reader must reverse-engineer meaning from context. `data` could be a dict, a DataFrame, a byte buffer, or a domain object. Bugs hide in plain sight because variable misuse is invisible when names carry no semantics. Code reviews become ineffective — reviewers cannot spot a logic error when they cannot distinguish `temp` from `result2`.
|
|
41
|
+
|
|
42
|
+
**The fix:**
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
# Before
|
|
46
|
+
def process(data, info, temp):
|
|
47
|
+
x = data["val"]
|
|
48
|
+
y = info.get("thing")
|
|
49
|
+
result2 = x + y
|
|
50
|
+
return result2
|
|
51
|
+
|
|
52
|
+
# After
|
|
53
|
+
def calculate_order_total(order: Order, pricing_config: PricingConfig) -> Decimal:
|
|
54
|
+
base_price = order.unit_price
|
|
55
|
+
discount_rate = pricing_config.get("discount_rate", Decimal("0"))
|
|
56
|
+
total = base_price * (1 - discount_rate)
|
|
57
|
+
return total
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Detection rule:**
|
|
61
|
+
Flag any identifier matching: `data`, `info`, `temp`, `tmp`, `result[0-9]*`, `val`, `thing`, `stuff`, `x`, `y`, `z`, `foo`, `bar`, `baz` outside of test fixtures and mathematical algorithms where single-letter names are conventional.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## AP-2: Misleading Names
|
|
66
|
+
|
|
67
|
+
**Also known as:** Lying names, deceptive identifiers, semantic mismatch
|
|
68
|
+
**Frequency:** Common
|
|
69
|
+
**Severity:** High
|
|
70
|
+
**Detection difficulty:** Hard
|
|
71
|
+
|
|
72
|
+
**What it looks like:**
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
// isReady actually means "has the component been mounted at least once"
|
|
76
|
+
// It returns false during SSR and true after first hydration
|
|
77
|
+
class ComponentTracker {
|
|
78
|
+
isReady() {
|
|
79
|
+
return this._mountCount > 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// "save" actually sends an HTTP request AND updates local cache AND emits an event
|
|
83
|
+
save(payload) {
|
|
84
|
+
return fetch('/api/save', { method: 'POST', body: JSON.stringify(payload) })
|
|
85
|
+
.then(() => { this._cache.set(payload.id, payload); this._emitter.emit('saved'); });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Why developers do it:**
|
|
91
|
+
The name was accurate when first written, then behavior evolved. Refactors add responsibilities without renaming. Or the developer chose an optimistic name hoping to match an intended final state.
|
|
92
|
+
|
|
93
|
+
**What goes wrong:**
|
|
94
|
+
Callers build mental models from the name, not from reading the implementation. Code that calls `if (tracker.isReady())` expects a readiness check, not a mount-count check. Bugs appear at integration boundaries where callers behave according to the name's implicit contract, not the implementation's actual contract.
|
|
95
|
+
|
|
96
|
+
**The fix:**
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
// Before
|
|
100
|
+
isReady() { return this._mountCount > 0; }
|
|
101
|
+
save(payload) { /* fetches, caches, and emits */ }
|
|
102
|
+
|
|
103
|
+
// After
|
|
104
|
+
hasBeenMountedAtLeastOnce() { return this._mountCount > 0; }
|
|
105
|
+
// or: wasMounted, isHydrated
|
|
106
|
+
|
|
107
|
+
persistAndNotify(payload) {
|
|
108
|
+
return this._apiClient.persist(payload)
|
|
109
|
+
.then(() => {
|
|
110
|
+
this._cache.set(payload.id, payload);
|
|
111
|
+
this._eventBus.emit('record:saved', payload);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Detection rule:**
|
|
117
|
+
Automated detection is limited. Heuristic: flag boolean-returning functions named `is*` or `has*` whose implementation contains arithmetic, comparisons against non-boolean values, or side effects.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## AP-3: Abbreviations That Obscure Meaning
|
|
122
|
+
|
|
123
|
+
**Also known as:** Cryptic abbreviations, terse naming, compressed identifiers
|
|
124
|
+
**Frequency:** Very Common
|
|
125
|
+
**Severity:** Medium
|
|
126
|
+
**Detection difficulty:** Moderate
|
|
127
|
+
|
|
128
|
+
**What it looks like:**
|
|
129
|
+
|
|
130
|
+
```java
|
|
131
|
+
public class OrdProc {
|
|
132
|
+
private CustMgr cm;
|
|
133
|
+
private InvSvc invSvc;
|
|
134
|
+
|
|
135
|
+
public OrdRes procOrd(OrdReq req, UsrCtx uctx) {
|
|
136
|
+
Cust c = cm.getCust(uctx.getCustId());
|
|
137
|
+
Inv inv = invSvc.chkInv(req.getProdId(), req.getQty());
|
|
138
|
+
// ...
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Why developers do it:**
|
|
144
|
+
Developers optimize for typing speed, imitate legacy codebases where line length was constrained, or confuse brevity with clarity. Domain-specific abbreviations feel obvious to their author.
|
|
145
|
+
|
|
146
|
+
**What goes wrong:**
|
|
147
|
+
Abbreviations have no canonical expansion. `OrdProc` could be OrderProcessor, OrderProcessing, OrderProcedure, or OrderProduct. New team members spend weeks building a mental glossary. Code search becomes unreliable — searching for "Order" misses `Ord` references. IDEs handle autocomplete, so typing cost is imaginary.
|
|
148
|
+
|
|
149
|
+
**The fix:**
|
|
150
|
+
|
|
151
|
+
```java
|
|
152
|
+
// Before
|
|
153
|
+
public OrdRes procOrd(OrdReq req, UsrCtx uctx)
|
|
154
|
+
|
|
155
|
+
// After
|
|
156
|
+
public OrderResponse processOrder(OrderRequest request, UserContext userContext)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Detection rule:**
|
|
160
|
+
Flag identifiers shorter than 4 characters (excluding loop counters `i`, `j`, `k` and mathematical variables), and identifiers matching common abbreviation patterns: `[A-Z][a-z]{0,2}[A-Z]` (camelCase abbreviations), `Mgr`, `Svc`, `Proc`, `Req`, `Res`, `Ctx`, `Usr`, `Cust`, `Ord`, `Inv`, `Prd`.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## AP-4: Hungarian Notation in Modern Languages
|
|
165
|
+
|
|
166
|
+
**Also known as:** Type-encoded names, systems Hungarian, strPrefix naming
|
|
167
|
+
**Frequency:** Common (in legacy codebases), Occasional (in new code)
|
|
168
|
+
**Severity:** Medium
|
|
169
|
+
**Detection difficulty:** Easy
|
|
170
|
+
|
|
171
|
+
**What it looks like:**
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
const strUserName: string = "Alice";
|
|
175
|
+
const nItemCount: number = 42;
|
|
176
|
+
const bIsActive: boolean = true;
|
|
177
|
+
const arrProductList: Product[] = [];
|
|
178
|
+
const objUserConfig: UserConfig = {};
|
|
179
|
+
const fnHandleClick = (e: Event) => {};
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Why developers do it:**
|
|
183
|
+
Hungarian notation was recommended in early Windows API documentation and spread widely. Developers carry it forward from C/C++ habits into typed languages. The original intent — encoding *kind* (not type) in names — was sound, but systems Hungarian (encoding types) became the dominant misapplication.
|
|
184
|
+
|
|
185
|
+
**What goes wrong:**
|
|
186
|
+
Modern IDEs show types on hover. Compilers enforce types at compile time. The prefix provides zero information not already available from the type system, while adding noise that the brain must filter on every read. When types change during refactoring (e.g., `strCount` becomes a number), the prefix becomes actively misleading. As Herb Sutter noted, the notation forces code to read like "hieroglyphics" rather than natural language.
|
|
187
|
+
|
|
188
|
+
**The fix:**
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Before
|
|
192
|
+
const strUserName: string = "Alice";
|
|
193
|
+
const arrProductList: Product[] = [];
|
|
194
|
+
const bIsActive: boolean = true;
|
|
195
|
+
|
|
196
|
+
// After
|
|
197
|
+
const userName: string = "Alice";
|
|
198
|
+
const products: Product[] = [];
|
|
199
|
+
const isActive: boolean = true;
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Detection rule:**
|
|
203
|
+
Flag identifiers with type-encoding prefixes: `str`, `n`, `i`, `b`, `arr`, `obj`, `fn`, `d`, `f`, `sz`, `lp`, `p`, `h` followed immediately by a capitalized word, in languages with static or structural type systems (TypeScript, Java, C#, Kotlin, Swift, Go).
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## AP-5: Naming Booleans Without Semantic Prefix
|
|
208
|
+
|
|
209
|
+
**Also known as:** Ambiguous boolean names, undeclared boolean intent
|
|
210
|
+
**Frequency:** Very Common
|
|
211
|
+
**Severity:** Medium
|
|
212
|
+
**Detection difficulty:** Easy
|
|
213
|
+
|
|
214
|
+
**What it looks like:**
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
user = User(active=True, delete=False, login=True, cache=False)
|
|
218
|
+
|
|
219
|
+
if user.active and not user.delete:
|
|
220
|
+
if user.login:
|
|
221
|
+
pass
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Why developers do it:**
|
|
225
|
+
Brevity. The noun or verb form feels "obvious" to the author in context.
|
|
226
|
+
|
|
227
|
+
**What goes wrong:**
|
|
228
|
+
`user.active` could mean "is the user currently active" (state) or "make this user active" (command). `user.delete` is especially dangerous — it reads as a method invocation, not a flag. Reading `not user.delete` requires conscious re-parsing. `user.login` could be a noun (the login value), a verb (perform login), or a state (currently logged in). Booleans without `is/has/should/can/was/will` prefixes are systematically ambiguous.
|
|
229
|
+
|
|
230
|
+
**The fix:**
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
# Before
|
|
234
|
+
user = User(active=True, delete=False, login=True, cache=False)
|
|
235
|
+
|
|
236
|
+
# After
|
|
237
|
+
user = User(
|
|
238
|
+
is_active=True,
|
|
239
|
+
is_deleted=False,
|
|
240
|
+
is_logged_in=True,
|
|
241
|
+
should_cache=False
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
if user.is_active and not user.is_deleted:
|
|
245
|
+
if user.is_logged_in:
|
|
246
|
+
pass
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**Detection rule:**
|
|
250
|
+
Flag boolean-typed fields, parameters, and variables whose names do not begin with: `is_`, `has_`, `should_`, `can_`, `was_`, `will_`, `needs_`, `allows_`, `enables_`, `supports_` (or their camelCase equivalents).
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## AP-6: Inconsistent Naming Across Codebase
|
|
255
|
+
|
|
256
|
+
**Also known as:** Naming drift, synonym proliferation, vocabulary fragmentation
|
|
257
|
+
**Frequency:** Very Common
|
|
258
|
+
**Severity:** High
|
|
259
|
+
**Detection difficulty:** Moderate
|
|
260
|
+
|
|
261
|
+
**What it looks like:**
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
# In user_service.py
|
|
265
|
+
def get_user(user_id): ...
|
|
266
|
+
def fetch_account(account_id): ...
|
|
267
|
+
def load_profile(profile_id): ...
|
|
268
|
+
def retrieve_member(member_id): ...
|
|
269
|
+
|
|
270
|
+
# In the database layer
|
|
271
|
+
class UserRepository: ...
|
|
272
|
+
class AccountDao: ...
|
|
273
|
+
class ProfileStore: ...
|
|
274
|
+
class MemberModel: ...
|
|
275
|
+
|
|
276
|
+
# In the API layer
|
|
277
|
+
def create_user(): ...
|
|
278
|
+
def register_account(): ...
|
|
279
|
+
def signup_member(): ...
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**Why developers do it:**
|
|
283
|
+
Different developers write different layers. Naming conventions are not documented or enforced. The codebase grows organically across teams and time.
|
|
284
|
+
|
|
285
|
+
**What goes wrong:**
|
|
286
|
+
Readers cannot tell if `User`, `Account`, `Profile`, and `Member` refer to the same entity or distinct concepts. Every new developer must map the entire synonym set before reasoning about data flow. Search and refactoring tools produce incomplete results. Domain modeling becomes impossible when the domain vocabulary is undefined.
|
|
287
|
+
|
|
288
|
+
**The fix:**
|
|
289
|
+
Establish and document a ubiquitous language (from Domain-Driven Design). Pick one term per concept codebase-wide:
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
# Chosen vocabulary: "user" for the core entity, "fetch" for retrieval
|
|
293
|
+
def fetch_user(user_id): ...
|
|
294
|
+
class UserRepository: ...
|
|
295
|
+
def create_user(): ...
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**Detection rule:**
|
|
299
|
+
Build a synonym set from the codebase's identifier vocabulary. Flag entities that appear under multiple names within the same domain layer: detect via clustering of identifiers that share data-type relationships (same field types, same return types) but differ in naming.
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## AP-7: The Wrong Abstraction
|
|
304
|
+
|
|
305
|
+
**Also known as:** Inappropriate DRY, premature unification, forced reuse
|
|
306
|
+
**Frequency:** Common
|
|
307
|
+
**Severity:** High
|
|
308
|
+
**Detection difficulty:** Hard
|
|
309
|
+
|
|
310
|
+
**What it looks like:**
|
|
311
|
+
|
|
312
|
+
```ruby
|
|
313
|
+
# Started as a clean abstraction for rendering two similar forms
|
|
314
|
+
def render_form(type:, show_address:, show_phone:, show_email:,
|
|
315
|
+
require_address:, address_label: "Address",
|
|
316
|
+
show_secondary_address:, show_company:,
|
|
317
|
+
company_required: false, skip_validation:,
|
|
318
|
+
legacy_mode:, use_new_layout: false)
|
|
319
|
+
if legacy_mode
|
|
320
|
+
render_legacy_form(...)
|
|
321
|
+
elsif type == :checkout
|
|
322
|
+
# ... 40 lines specific to checkout
|
|
323
|
+
elsif type == :profile
|
|
324
|
+
# ... 40 lines specific to profile
|
|
325
|
+
else
|
|
326
|
+
# ... default path that is now used by nobody
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Why developers do it:**
|
|
332
|
+
Two pieces of code look similar. A developer extracts them into one function to avoid duplication (applying DRY). Later, requirements diverge and the abstraction accumulates parameters to handle each case. As Sandi Metz documented, each new programmer feels obligated to preserve the existing abstraction and adds another parameter rather than questioning it.
|
|
333
|
+
|
|
334
|
+
**What goes wrong:**
|
|
335
|
+
The abstraction becomes a catch-all procedure with combinatorial complexity. Every caller must understand all parameters, most of which are irrelevant to it. Adding a new caller requires understanding (and not breaking) every existing caller's path. The code is harder to read than the original duplication would have been.
|
|
336
|
+
|
|
337
|
+
**The fix:**
|
|
338
|
+
Metz's prescription: inline the abstraction back into every caller, then let each caller have only what it needs. Accept duplication until a genuinely shared pattern emerges.
|
|
339
|
+
|
|
340
|
+
```ruby
|
|
341
|
+
# Before: one function with 10 parameters serving 3 callers
|
|
342
|
+
|
|
343
|
+
# After: three focused functions
|
|
344
|
+
def render_checkout_form(show_address:, require_address:)
|
|
345
|
+
# only checkout-relevant logic
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def render_profile_form(show_phone:, show_email:, show_company:)
|
|
349
|
+
# only profile-relevant logic
|
|
350
|
+
end
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Detection rule:**
|
|
354
|
+
Flag functions with: (a) more than 5 boolean/flag parameters, or (b) internal branching on a `type:` / `mode:` / `variant:` parameter with 3 or more branches, or (c) a parameter count that has grown by more than 3 over the git history of the function.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## AP-8: Premature Abstraction
|
|
359
|
+
|
|
360
|
+
**Also known as:** Speculative generality, YAGNI violation, over-engineering
|
|
361
|
+
**Frequency:** Common
|
|
362
|
+
**Severity:** High
|
|
363
|
+
**Detection difficulty:** Moderate
|
|
364
|
+
|
|
365
|
+
**What it looks like:**
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
// Written for a single use case: logging to a file
|
|
369
|
+
interface Logger {
|
|
370
|
+
log(level: LogLevel, message: string, context?: Record<string, unknown>): void;
|
|
371
|
+
flush(): Promise<void>;
|
|
372
|
+
close(): void;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
abstract class BaseLogger implements Logger {
|
|
376
|
+
protected abstract formatMessage(level: LogLevel, message: string): string;
|
|
377
|
+
protected abstract writeRecord(record: string): Promise<void>;
|
|
378
|
+
// ...
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
class FileLogger extends BaseLogger {
|
|
382
|
+
// The only implementation that ever existed
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// FileLogger is created through a factory
|
|
386
|
+
class LoggerFactory {
|
|
387
|
+
static create(config: LoggerConfig): Logger {
|
|
388
|
+
return new FileLogger(config); // always FileLogger
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**Why developers do it:**
|
|
394
|
+
Developers correctly recognize that "I might need other implementations later." The logger example above is pervasive: it appears in tutorials as a best practice. The problem is not the pattern — it is applying the pattern before the second use case exists.
|
|
395
|
+
|
|
396
|
+
**What goes wrong:**
|
|
397
|
+
Every new team member must navigate the interface, the abstract base, the factory, and the concrete class to understand "we write logs to a file." The abstraction adds four files, two levels of inheritance, and a factory with one code path. When the second use case never arrives (often the case), all this structure becomes permanent overhead. Premature abstraction is the root cause of enterprise framework bloat.
|
|
398
|
+
|
|
399
|
+
**The fix:**
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
// Before: interface + abstract base + factory + concrete = 4 files, 0 second implementations
|
|
403
|
+
// After: start with the concrete, extract interface when the second use case arrives
|
|
404
|
+
|
|
405
|
+
class FileLogger {
|
|
406
|
+
private readonly stream: WriteStream;
|
|
407
|
+
|
|
408
|
+
constructor(private readonly config: LoggerConfig) {
|
|
409
|
+
this.stream = createWriteStream(config.path, { flags: 'a' });
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
log(level: LogLevel, message: string): void {
|
|
413
|
+
this.stream.write(`[${level}] ${message}\n`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Detection rule:**
|
|
419
|
+
Flag: (a) interfaces with exactly one implementing class in the codebase, (b) abstract base classes with exactly one concrete subclass, (c) factory classes whose factory method returns a single concrete type unconditionally.
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## AP-9: Leaky Abstractions
|
|
424
|
+
|
|
425
|
+
**Also known as:** Implementation bleed-through, abstraction boundary violation
|
|
426
|
+
**Frequency:** Common
|
|
427
|
+
**Severity:** High
|
|
428
|
+
**Detection difficulty:** Hard
|
|
429
|
+
|
|
430
|
+
**What it looks like:**
|
|
431
|
+
|
|
432
|
+
```python
|
|
433
|
+
class UserRepository:
|
|
434
|
+
"""Abstracts database access for users."""
|
|
435
|
+
|
|
436
|
+
def find_users_by_status(self, status: str) -> list[User]:
|
|
437
|
+
# Leaks: caller must know MySQL ENUM values and NULL behavior
|
|
438
|
+
return self._db.query(
|
|
439
|
+
"SELECT * FROM users WHERE status = %s AND deleted_at IS NULL",
|
|
440
|
+
(status,)
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
def find_recent_users(self) -> list[User]:
|
|
444
|
+
# Leaks: caller must know the index name for performance
|
|
445
|
+
# Documented in a comment that belongs in the database schema
|
|
446
|
+
# Use the idx_created_at index — do not add ORDER BY or it will full-scan
|
|
447
|
+
return self._db.query("SELECT * FROM users USE INDEX (idx_created_at) LIMIT 100")
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**Why developers do it:**
|
|
451
|
+
Building abstractions is hard. Developers reach through the abstraction layer for performance, for pragmatism, or because the abstraction boundary was never clearly defined.
|
|
452
|
+
|
|
453
|
+
**What goes wrong:**
|
|
454
|
+
Joel Spolsky's Law of Leaky Abstractions states: "All non-trivial abstractions, to some degree, are leaky." The problem is *how much* they leak. When a repository leaks SQL syntax, callers cannot be swapped to a different database without understanding and rewriting every caller. Performance surprises — like the index hint above — require knowing the underlying storage engine. The abstraction provides the vocabulary of the layer above without the isolation.
|
|
455
|
+
|
|
456
|
+
**The fix:**
|
|
457
|
+
|
|
458
|
+
```python
|
|
459
|
+
class UserRepository:
|
|
460
|
+
def find_active_users(self) -> list[User]:
|
|
461
|
+
# Caller knows: "give me active users" — not how "active" is stored
|
|
462
|
+
return self._db.query(
|
|
463
|
+
"SELECT * FROM users WHERE status = 'active' AND deleted_at IS NULL"
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
def find_recently_registered(self, limit: int = 100) -> list[User]:
|
|
467
|
+
# Performance details are an implementation concern, not caller's concern
|
|
468
|
+
return self._db.query(
|
|
469
|
+
"SELECT * FROM users USE INDEX (idx_created_at) "
|
|
470
|
+
"ORDER BY created_at DESC LIMIT %s", (limit,)
|
|
471
|
+
)
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
**Detection rule:**
|
|
475
|
+
Flag: (a) SQL strings in non-repository/DAO classes, (b) database-specific syntax (USE INDEX, NOLOCK, ROWNUM) outside the data access layer, (c) HTTP status codes or header names in business logic classes, (d) filesystem paths in domain objects.
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## AP-10: Abstraction Inversion
|
|
480
|
+
|
|
481
|
+
**Also known as:** Upside-down abstraction, re-implementing built-ins through the abstraction
|
|
482
|
+
**Frequency:** Occasional
|
|
483
|
+
**Severity:** High
|
|
484
|
+
**Detection difficulty:** Hard
|
|
485
|
+
|
|
486
|
+
**What it looks like:**
|
|
487
|
+
|
|
488
|
+
```csharp
|
|
489
|
+
// The ORM exposes SaveChanges() which handles transactions internally.
|
|
490
|
+
// The repository hides SaveChanges() behind a "unit of work" abstraction.
|
|
491
|
+
// Now callers need transaction semantics, but the abstraction doesn't expose them.
|
|
492
|
+
// Solution: fetch all records and filter in application code.
|
|
493
|
+
|
|
494
|
+
public class OrderService {
|
|
495
|
+
public async Task<List<Order>> GetPendingOrdersForCustomer(int customerId) {
|
|
496
|
+
// Cannot call SQL WHERE because the repository hides it.
|
|
497
|
+
// Must fetch ALL orders and filter here.
|
|
498
|
+
var allOrders = await _repository.GetAllOrders(); // 50,000 rows
|
|
499
|
+
return allOrders.Where(o => o.CustomerId == customerId
|
|
500
|
+
&& o.Status == OrderStatus.Pending).ToList();
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**Why developers do it:**
|
|
506
|
+
Abstraction inversion happens when an abstraction is designed for one use case (e.g., simple CRUD) and then used for a more complex one (e.g., filtered queries). Rather than extend the abstraction properly, developers implement the missing functionality on top of the abstraction's public API, which re-uses the abstraction's internals without direct access.
|
|
507
|
+
|
|
508
|
+
**What goes wrong:**
|
|
509
|
+
Performance degrades (fetching 50,000 rows to return 3). The workaround becomes load-bearing, persists indefinitely, and is never cleaned up. The abstraction now has the worst of both worlds: it hides complexity without providing the tools to manage that complexity.
|
|
510
|
+
|
|
511
|
+
**The fix:**
|
|
512
|
+
|
|
513
|
+
```csharp
|
|
514
|
+
// Expose the needed capability at the correct abstraction level
|
|
515
|
+
public interface IOrderRepository {
|
|
516
|
+
Task<List<Order>> GetPendingOrdersByCustomer(int customerId);
|
|
517
|
+
// or: Task<List<Order>> FindOrders(OrderQuery query);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
public class SqlOrderRepository : IOrderRepository {
|
|
521
|
+
public async Task<List<Order>> GetPendingOrdersByCustomer(int customerId) {
|
|
522
|
+
return await _context.Orders
|
|
523
|
+
.Where(o => o.CustomerId == customerId && o.Status == OrderStatus.Pending)
|
|
524
|
+
.ToListAsync();
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
**Detection rule:**
|
|
530
|
+
Flag: (a) `GetAll()` calls followed by in-memory LINQ/filter operations in a service layer, (b) loading full collections to find single entities by non-primary-key fields, (c) N+1 query patterns where one call retrieves parents and a loop retrieves children individually.
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## AP-11: Too Many Layers of Indirection
|
|
535
|
+
|
|
536
|
+
**Also known as:** Over-engineering, Enterprise FizzBuzz, abstraction tax
|
|
537
|
+
**Frequency:** Common
|
|
538
|
+
**Severity:** Medium
|
|
539
|
+
**Detection difficulty:** Moderate
|
|
540
|
+
|
|
541
|
+
**What it looks like:**
|
|
542
|
+
|
|
543
|
+
```
|
|
544
|
+
GET /users/{id}
|
|
545
|
+
→ UserController
|
|
546
|
+
→ UserApplicationService
|
|
547
|
+
→ UserDomainService
|
|
548
|
+
→ UserRepository
|
|
549
|
+
→ UserDataMapper
|
|
550
|
+
→ UserEntityFactory
|
|
551
|
+
→ UserDao
|
|
552
|
+
→ DatabaseConnectionPool
|
|
553
|
+
→ SELECT * FROM users WHERE id = ?
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
Ten hops to read one database row. Each layer has its own class, its own interface, and its own test file. The `UserEntityFactory` has one method: `create(row: Row): UserEntity`.
|
|
557
|
+
|
|
558
|
+
**Why developers do it:**
|
|
559
|
+
Architecture guidelines recommend separation of concerns. Clean Architecture diagrams show layered rings. Developers apply these patterns uniformly regardless of domain complexity, producing enterprise patterns around what is effectively a CRUD operation.
|
|
560
|
+
|
|
561
|
+
**What goes wrong:**
|
|
562
|
+
Debugging requires tracing a call stack ten levels deep. Onboarding takes days instead of hours. Every new feature requires touching 8-10 files. The indirection layers add no semantic content — they rename data without transforming it. The fundamental theorem of software engineering ("any problem can be solved by adding a layer of indirection") has a corollary: the solution may introduce more problems than it solves.
|
|
563
|
+
|
|
564
|
+
**The fix:**
|
|
565
|
+
Apply layering proportional to actual complexity. A simple CRUD resource needs: a controller, a repository, and a domain object. Extract additional layers only when they carry distinct responsibilities that differ in rate of change.
|
|
566
|
+
|
|
567
|
+
```
|
|
568
|
+
GET /users/{id}
|
|
569
|
+
→ UserController (HTTP boundary: parse request, render response)
|
|
570
|
+
→ UserRepository (data boundary: query + map to domain object)
|
|
571
|
+
→ SELECT * FROM users WHERE id = ?
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
**Detection rule:**
|
|
575
|
+
Flag call chains where more than 4 consecutive classes are single-method pass-throughs (methods that call exactly one other method with no transformation logic).
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## AP-12: Util/Helper/Manager/Service God Classes
|
|
580
|
+
|
|
581
|
+
**Also known as:** Dunghill anti-pattern, junk drawer class, catch-all module
|
|
582
|
+
**Frequency:** Very Common
|
|
583
|
+
**Severity:** High
|
|
584
|
+
**Detection difficulty:** Easy
|
|
585
|
+
|
|
586
|
+
**What it looks like:**
|
|
587
|
+
|
|
588
|
+
```python
|
|
589
|
+
# utils.py — 2,847 lines
|
|
590
|
+
def format_date(d): ...
|
|
591
|
+
def validate_email(email): ...
|
|
592
|
+
def calculate_tax(amount, rate): ...
|
|
593
|
+
def send_slack_notification(message): ...
|
|
594
|
+
def parse_csv(path): ...
|
|
595
|
+
def generate_uuid(): ...
|
|
596
|
+
def truncate_string(s, max_len): ...
|
|
597
|
+
def get_current_user_from_request(req): ...
|
|
598
|
+
def deep_merge_dicts(a, b): ...
|
|
599
|
+
def retry_with_backoff(fn, retries): ...
|
|
600
|
+
# ... 200 more functions
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
**Why developers do it:**
|
|
604
|
+
A utility function does not obviously "belong" to any single domain class. Rather than create a new focused module, the developer drops it into the nearest catch-all. The moment `utils.py` is created, it becomes a gravity well — the path of least resistance for every "I don't know where this goes" function.
|
|
605
|
+
|
|
606
|
+
**What goes wrong:**
|
|
607
|
+
The `utils.py` file becomes untestable (every test must import 2,000 lines of unrelated code), unsearchable (nothing is where you would look for it), and unmaintainable (nobody owns it). Import cycles proliferate because everything imports utils and utils imports everything. New developers cannot discover capability that already exists.
|
|
608
|
+
|
|
609
|
+
**The fix:**
|
|
610
|
+
Every function belongs somewhere specific. When it seems not to belong, it usually belongs on the primary domain object it operates on:
|
|
611
|
+
|
|
612
|
+
```python
|
|
613
|
+
# Before: utils.format_date(order.created_at)
|
|
614
|
+
# After: order.formatted_created_at (property) or DateFormatter.format(order.created_at)
|
|
615
|
+
|
|
616
|
+
# Before: utils.calculate_tax(order.subtotal, region.tax_rate)
|
|
617
|
+
# After: order.calculate_tax(region) or TaxCalculator(region).calculate(order)
|
|
618
|
+
|
|
619
|
+
# Before: utils.send_slack_notification(message)
|
|
620
|
+
# After: SlackNotifier(config).send(message)
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
**Detection rule:**
|
|
624
|
+
Flag files/modules named `utils`, `helpers`, `common`, `misc`, `shared`, `tools`, `lib` that contain more than 15 functions, or any class named `*Util`, `*Helper`, `*Manager`, `*Handler` with more than 10 public methods spanning more than 3 distinct responsibility domains.
|
|
625
|
+
|
|
626
|
+
---
|
|
627
|
+
|
|
628
|
+
## AP-13: Naming by Implementation, Not Intention
|
|
629
|
+
|
|
630
|
+
**Also known as:** How-naming vs what-naming, implementation-leaking names
|
|
631
|
+
**Frequency:** Common
|
|
632
|
+
**Severity:** Medium
|
|
633
|
+
**Detection difficulty:** Moderate
|
|
634
|
+
|
|
635
|
+
**What it looks like:**
|
|
636
|
+
|
|
637
|
+
```java
|
|
638
|
+
public class UserService {
|
|
639
|
+
// Name describes mechanism, not purpose
|
|
640
|
+
public User iterateUsersAndFindByEmailUsingHashMap(String email) { ... }
|
|
641
|
+
|
|
642
|
+
// Name exposes storage format
|
|
643
|
+
public List<Order> getOrdersFromRedisCache(int userId) { ... }
|
|
644
|
+
|
|
645
|
+
// Name exposes algorithm
|
|
646
|
+
public List<Product> sortProductsWithQuicksort(List<Product> products) { ... }
|
|
647
|
+
|
|
648
|
+
// Name exposes network call
|
|
649
|
+
public UserProfile callProfileServiceAndDeserializeJson(String userId) { ... }
|
|
650
|
+
}
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
**Why developers do it:**
|
|
654
|
+
The implementation is fresh in the developer's mind while writing the function. Naming it by mechanism is cognitive shorthand. It also feels precise.
|
|
655
|
+
|
|
656
|
+
**What goes wrong:**
|
|
657
|
+
Implementation names create false coupling. When `getOrdersFromRedisCache` switches storage backends (Redis to Memcached, or cache to database), the name is wrong. Callers who read the name form expectations about caching behavior that may no longer hold. `sortProductsWithQuicksort` prevents the implementation from switching to mergesort without a rename cascade.
|
|
658
|
+
|
|
659
|
+
**The fix:**
|
|
660
|
+
|
|
661
|
+
```java
|
|
662
|
+
// Before
|
|
663
|
+
public User iterateUsersAndFindByEmailUsingHashMap(String email)
|
|
664
|
+
public List<Order> getOrdersFromRedisCache(int userId)
|
|
665
|
+
public List<Product> sortProductsWithQuicksort(List<Product> products)
|
|
666
|
+
|
|
667
|
+
// After
|
|
668
|
+
public Optional<User> findUserByEmail(String email)
|
|
669
|
+
public List<Order> getRecentOrders(int userId)
|
|
670
|
+
public List<Product> sortByPopularity(List<Product> products)
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
**Detection rule:**
|
|
674
|
+
Flag method names containing implementation-specific terms: `HashMap`, `ArrayList`, `Redis`, `Mysql`, `Postgres`, `Json`, `Xml`, `Quicksort`, `Bubblesort`, `Iterator`, `Loop`, `Iterate`, `Http`, `Socket`, `Cache` (when used as a mechanism indicator rather than a domain term).
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## AP-14: Generic Action Names
|
|
679
|
+
|
|
680
|
+
**Also known as:** Vague verbs, do-everything names, handle/process/manage
|
|
681
|
+
**Frequency:** Very Common
|
|
682
|
+
**Severity:** Medium
|
|
683
|
+
**Detection difficulty:** Easy
|
|
684
|
+
|
|
685
|
+
**What it looks like:**
|
|
686
|
+
|
|
687
|
+
```javascript
|
|
688
|
+
function handleData(data) { ... }
|
|
689
|
+
function processOrder(order) { ... }
|
|
690
|
+
function manageUsers(users) { ... }
|
|
691
|
+
function doStuff(input) { ... }
|
|
692
|
+
function runTask(task) { ... }
|
|
693
|
+
function executeRequest(request) { ... }
|
|
694
|
+
function performAction(action) { ... }
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
**Why developers do it:**
|
|
698
|
+
Generic verbs defer the decision about what the function actually does. They provide the illusion of naming while conveying no information. "Handle" is the default verb when a developer has not decided what the function's single responsibility is.
|
|
699
|
+
|
|
700
|
+
**What goes wrong:**
|
|
701
|
+
`handleData` communicates that a function receives data and does something with it — which is true of every function in every program. The caller cannot predict behavior, pre-conditions, post-conditions, or side effects from the name alone. When a bug appears inside `handleData`, the name provides no triage information. Functions with these names tend to accumulate unrelated behavior because their names permit it.
|
|
702
|
+
|
|
703
|
+
**The fix:**
|
|
704
|
+
|
|
705
|
+
```javascript
|
|
706
|
+
// Before: generic
|
|
707
|
+
function handleData(data) { /* validates, transforms, persists, and notifies */ }
|
|
708
|
+
|
|
709
|
+
// After: specific — one name per responsibility
|
|
710
|
+
function validateOrderPayload(payload) { ... }
|
|
711
|
+
function transformOrderToInvoice(order) { ... }
|
|
712
|
+
function persistInvoice(invoice) { ... }
|
|
713
|
+
function notifyCustomerOfInvoice(invoice, customer) { ... }
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
**Detection rule:**
|
|
717
|
+
Flag function/method names consisting solely of: `handle`, `process`, `manage`, `do`, `run`, `execute`, `perform`, `operate`, `deal`, `take_care_of`, with no domain-specific qualifier following the verb.
|
|
718
|
+
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
## AP-15: Negated Boolean Names
|
|
722
|
+
|
|
723
|
+
**Also known as:** Double-negative conditions, inverted flags, isNot* naming
|
|
724
|
+
**Frequency:** Common
|
|
725
|
+
**Severity:** Medium
|
|
726
|
+
**Detection difficulty:** Easy
|
|
727
|
+
|
|
728
|
+
**What it looks like:**
|
|
729
|
+
|
|
730
|
+
```python
|
|
731
|
+
class User:
|
|
732
|
+
is_not_banned: bool
|
|
733
|
+
is_not_deleted: bool
|
|
734
|
+
cannot_login: bool
|
|
735
|
+
has_no_subscription: bool
|
|
736
|
+
|
|
737
|
+
# Call site creates double negatives
|
|
738
|
+
if not user.is_not_banned:
|
|
739
|
+
# Is this: banned? or not-not-banned?
|
|
740
|
+
block_user(user)
|
|
741
|
+
|
|
742
|
+
if user.is_not_deleted and not user.cannot_login:
|
|
743
|
+
# Reader must parse two negations simultaneously
|
|
744
|
+
allow_login(user)
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
**Why developers do it:**
|
|
748
|
+
The feature is added incrementally. The default state starts as the negative case ("most users are not banned"), so the flag is named for the default. Or the developer reasons in terms of exceptions and names the flag for the exception.
|
|
749
|
+
|
|
750
|
+
**What goes wrong:**
|
|
751
|
+
`not user.is_not_banned` requires readers to evaluate a double negative, which reliably causes off-by-one reasoning errors. Studies on boolean logic errors consistently show that double negatives increase error rates. Code review becomes inadequate because reviewers mentally "simplify" the double negative and miss bugs in the simplification.
|
|
752
|
+
|
|
753
|
+
**The fix:**
|
|
754
|
+
|
|
755
|
+
```python
|
|
756
|
+
# Before
|
|
757
|
+
is_not_banned: bool
|
|
758
|
+
cannot_login: bool
|
|
759
|
+
|
|
760
|
+
# After — flip to positive form
|
|
761
|
+
is_banned: bool # default: False
|
|
762
|
+
can_login: bool # default: True
|
|
763
|
+
has_subscription: bool
|
|
764
|
+
is_deleted: bool
|
|
765
|
+
|
|
766
|
+
if user.is_banned:
|
|
767
|
+
block_user(user)
|
|
768
|
+
|
|
769
|
+
if not user.is_deleted and user.can_login:
|
|
770
|
+
allow_login(user)
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
**Detection rule:**
|
|
774
|
+
Flag boolean identifiers matching patterns: `is_not_*`, `isNot*`, `cannot_*`, `cannot*`, `has_no_*`, `hasNo*`, `no_*` (as boolean prefix), `never_*`, `without_*`, `lacks_*`.
|
|
775
|
+
|
|
776
|
+
---
|
|
777
|
+
|
|
778
|
+
## AP-16: Encoding Type in Name
|
|
779
|
+
|
|
780
|
+
**Also known as:** Type suffix naming, redundant type annotations in names
|
|
781
|
+
**Frequency:** Common
|
|
782
|
+
**Severity:** Low
|
|
783
|
+
**Detection difficulty:** Easy
|
|
784
|
+
|
|
785
|
+
**What it looks like:**
|
|
786
|
+
|
|
787
|
+
```python
|
|
788
|
+
user_list = [] # it's a list, we said so twice
|
|
789
|
+
user_dict = {} # same problem
|
|
790
|
+
product_string = ""
|
|
791
|
+
price_integer = 0
|
|
792
|
+
config_object = {}
|
|
793
|
+
callback_function = lambda: None
|
|
794
|
+
error_exception = ValueError("bad input")
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
**Why developers do it:**
|
|
798
|
+
A variant of Hungarian notation. Developers want to communicate the data structure to be used "just in case." Also appears when refactoring — a variable named `users` gets renamed to `user_list` to distinguish it from a newly added `users_dict`.
|
|
799
|
+
|
|
800
|
+
**What goes wrong:**
|
|
801
|
+
Types change. `user_list` becomes a generator, a queryset, or a tuple. The name is now wrong and misleading. The type suffix adds noise that readers learn to ignore, which means it contributes nothing after the first read. In statically typed languages the compiler makes it redundant; in dynamically typed languages the type hint makes it redundant.
|
|
802
|
+
|
|
803
|
+
**The fix:**
|
|
804
|
+
|
|
805
|
+
```python
|
|
806
|
+
# Before
|
|
807
|
+
user_list = []
|
|
808
|
+
user_dict = {}
|
|
809
|
+
|
|
810
|
+
# After — name the content/purpose, let the type be inferred or annotated
|
|
811
|
+
users: list[User] = []
|
|
812
|
+
users_by_id: dict[int, User] = {}
|
|
813
|
+
active_users: QuerySet[User] = User.objects.filter(is_active=True)
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
**Detection rule:**
|
|
817
|
+
Flag identifiers ending in `_list`, `_dict`, `_array`, `_map`, `_set`, `_tuple`, `_string`, `_int`, `_float`, `_bool`, `_object`, `_function`, `_callback`, `_exception`, `_error` (as type suffix, not domain term).
|
|
818
|
+
|
|
819
|
+
---
|
|
820
|
+
|
|
821
|
+
## AP-17: Comments Compensating for Bad Names
|
|
822
|
+
|
|
823
|
+
**Also known as:** Explanatory comments as naming shortcuts, comment-instead-of-rename
|
|
824
|
+
**Frequency:** Very Common
|
|
825
|
+
**Severity:** Medium
|
|
826
|
+
**Detection difficulty:** Moderate
|
|
827
|
+
|
|
828
|
+
**What it looks like:**
|
|
829
|
+
|
|
830
|
+
```javascript
|
|
831
|
+
// Checks if user can proceed (not just if they're logged in — also checks
|
|
832
|
+
// subscription status, account standing, and rate limit)
|
|
833
|
+
function isReady(user) { ... }
|
|
834
|
+
|
|
835
|
+
// The multiplier for regional pricing adjustment
|
|
836
|
+
const x = 1.23;
|
|
837
|
+
|
|
838
|
+
// Loop through users and filter out inactive ones, then sort by signup date
|
|
839
|
+
function processData(users) { ... }
|
|
840
|
+
|
|
841
|
+
// Flag that tracks whether we've already sent the welcome email this session
|
|
842
|
+
let sent = false;
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
**Why developers do it:**
|
|
846
|
+
The developer knows the comment is necessary and writes it — but does not take the next step of embedding that comment in the name. It feels faster to add a comment than to rename and update all references.
|
|
847
|
+
|
|
848
|
+
**What goes wrong:**
|
|
849
|
+
Comments lie. The code is the truth; comments are assertions about the code that go stale. When `isReady` evolves to add a fourth check, the comment may or may not be updated. The name `isReady` persists and continues to mislead. Comments are also invisible in call sites — readers of `if (isReady(user))` elsewhere in the codebase see no comment, only the misleading name.
|
|
850
|
+
|
|
851
|
+
**The fix:**
|
|
852
|
+
|
|
853
|
+
```javascript
|
|
854
|
+
// Before
|
|
855
|
+
// Checks if user can proceed to checkout
|
|
856
|
+
function isReady(user) { ... }
|
|
857
|
+
|
|
858
|
+
// After — name replaces the comment
|
|
859
|
+
function canProceedToCheckout(user) { ... }
|
|
860
|
+
|
|
861
|
+
// Before
|
|
862
|
+
const x = 1.23; // regional pricing multiplier
|
|
863
|
+
|
|
864
|
+
// After
|
|
865
|
+
const REGIONAL_PRICING_MULTIPLIER = 1.23;
|
|
866
|
+
|
|
867
|
+
// Before
|
|
868
|
+
// loop through users, filter inactive, sort by signup
|
|
869
|
+
function processData(users) { ... }
|
|
870
|
+
|
|
871
|
+
// After
|
|
872
|
+
function getActiveUsersSortedBySignupDate(users) { ... }
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
**Detection rule:**
|
|
876
|
+
Flag inline comments (single-line `//` or `#` comments) that appear on the same line as, or immediately preceding, a variable declaration or function definition. These are almost always naming failures waiting to be fixed.
|
|
877
|
+
|
|
878
|
+
---
|
|
879
|
+
|
|
880
|
+
## AP-18: Renaming Without Updating All References
|
|
881
|
+
|
|
882
|
+
**Also known as:** Partial rename, ghost identifiers, alias drift
|
|
883
|
+
**Frequency:** Common
|
|
884
|
+
**Severity:** High
|
|
885
|
+
**Detection difficulty:** Moderate
|
|
886
|
+
|
|
887
|
+
**What it looks like:**
|
|
888
|
+
|
|
889
|
+
```python
|
|
890
|
+
# Original function
|
|
891
|
+
def get_user(user_id: int) -> User:
|
|
892
|
+
return db.query(User).filter_by(id=user_id).first()
|
|
893
|
+
|
|
894
|
+
# Renamed in user_service.py during a refactor
|
|
895
|
+
def fetch_user_by_id(user_id: int) -> User:
|
|
896
|
+
return db.query(User).filter_by(id=user_id).first()
|
|
897
|
+
|
|
898
|
+
# But 23 other files still call get_user()
|
|
899
|
+
# So both coexist, one as an alias or duplicate
|
|
900
|
+
def get_user(user_id: int) -> User:
|
|
901
|
+
return fetch_user_by_id(user_id) # thin wrapper preserved "for compatibility"
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
**Why developers do it:**
|
|
905
|
+
Renaming is interrupted. The refactor tool misses dynamic call sites. The developer renames in one module but does not run a global search. Or the rename is intentional: the old name is kept as a "backwards compatible alias" that is never removed.
|
|
906
|
+
|
|
907
|
+
**What goes wrong:**
|
|
908
|
+
The codebase develops two canonical names for the same concept. New code uses the new name; old code uses the old name. The transition never completes. Six months later, neither name is clearly primary, and every junior developer wonders whether `get_user` and `fetch_user_by_id` do different things. Bugs appear when the wrapper alias and the real function diverge.
|
|
909
|
+
|
|
910
|
+
**The fix:**
|
|
911
|
+
Complete renames atomically using IDE refactoring tools. When renaming across a public API boundary, deprecate with a sunset date and a migration guide:
|
|
912
|
+
|
|
913
|
+
```python
|
|
914
|
+
import warnings
|
|
915
|
+
|
|
916
|
+
def get_user(user_id: int) -> User:
|
|
917
|
+
warnings.warn(
|
|
918
|
+
"get_user() is deprecated; use fetch_user_by_id() instead. "
|
|
919
|
+
"Will be removed in v3.0.",
|
|
920
|
+
DeprecationWarning,
|
|
921
|
+
stacklevel=2
|
|
922
|
+
)
|
|
923
|
+
return fetch_user_by_id(user_id)
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
**Detection rule:**
|
|
927
|
+
Flag functions/methods where: (a) the body is a single call to another function with the same parameter signature (thin wrappers), (b) the old name appears in a deprecation warning, (c) git log shows a rename event without a corresponding sweep of all call sites.
|
|
928
|
+
|
|
929
|
+
---
|
|
930
|
+
|
|
931
|
+
## AP-19: Abstract Class with One Implementation
|
|
932
|
+
|
|
933
|
+
**Also known as:** Speculative inheritance, phantom polymorphism, one-class hierarchy
|
|
934
|
+
**Frequency:** Common
|
|
935
|
+
**Severity:** Medium
|
|
936
|
+
**Detection difficulty:** Easy
|
|
937
|
+
|
|
938
|
+
**What it looks like:**
|
|
939
|
+
|
|
940
|
+
```java
|
|
941
|
+
public abstract class NotificationSender {
|
|
942
|
+
protected abstract void connect();
|
|
943
|
+
protected abstract void disconnect();
|
|
944
|
+
public abstract void send(Notification notification);
|
|
945
|
+
protected abstract boolean isConnected();
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
public class EmailNotificationSender extends NotificationSender {
|
|
949
|
+
// The only subclass. No other implementation has ever been written.
|
|
950
|
+
@Override protected void connect() { /* SMTP connect */ }
|
|
951
|
+
@Override protected void disconnect() { /* SMTP disconnect */ }
|
|
952
|
+
@Override public void send(Notification notification) { /* send email */ }
|
|
953
|
+
@Override protected boolean isConnected() { return this.smtpClient.isConnected(); }
|
|
954
|
+
}
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
**Why developers do it:**
|
|
958
|
+
"We might add SMS or push notifications later." The abstract base class is written in anticipation of future polymorphism. The architecture feels clean and extensible.
|
|
959
|
+
|
|
960
|
+
**What goes wrong:**
|
|
961
|
+
The abstract class defines a template that the single concrete class must conform to, even if the template does not fit the concrete implementation well. Adding SMS later requires conforming to an interface designed for SMTP. The abstract class calcifies the wrong abstraction (AP-7) before any second use case has revealed what the right abstraction would be. The overhead: two files instead of one, a template method pattern where none is needed, and forced overrides of methods that may not apply to all future implementors.
|
|
962
|
+
|
|
963
|
+
**The fix:**
|
|
964
|
+
|
|
965
|
+
```java
|
|
966
|
+
// Start with the concrete class
|
|
967
|
+
public class EmailNotificationSender {
|
|
968
|
+
public void send(Notification notification) { ... }
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// When SMS arrives, THEN extract the interface based on what both share
|
|
972
|
+
public interface NotificationSender {
|
|
973
|
+
void send(Notification notification);
|
|
974
|
+
}
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
**Detection rule:**
|
|
978
|
+
Flag abstract classes and interfaces with exactly one non-abstract/non-test implementing class in the codebase. Cross-reference with: (a) the abstract class was introduced in the same commit as its only implementation, (b) no second implementation exists anywhere in the git history.
|
|
979
|
+
|
|
980
|
+
---
|
|
981
|
+
|
|
982
|
+
## AP-20: Interface for Every Class
|
|
983
|
+
|
|
984
|
+
**Also known as:** IRepository/IService suffix proliferation, unnecessary interface extraction
|
|
985
|
+
**Frequency:** Common
|
|
986
|
+
**Severity:** Medium
|
|
987
|
+
**Detection difficulty:** Easy
|
|
988
|
+
|
|
989
|
+
**What it looks like:**
|
|
990
|
+
|
|
991
|
+
```csharp
|
|
992
|
+
// Every class has a matching interface, regardless of need
|
|
993
|
+
public interface IUserService { User GetUser(int id); void UpdateUser(User user); }
|
|
994
|
+
public class UserService : IUserService { ... }
|
|
995
|
+
|
|
996
|
+
public interface IOrderRepository { Order FindById(int id); void Save(Order order); }
|
|
997
|
+
public class OrderRepository : IOrderRepository { ... }
|
|
998
|
+
|
|
999
|
+
public interface IEmailSender { void Send(EmailMessage message); }
|
|
1000
|
+
public class EmailSender : IEmailSender { ... }
|
|
1001
|
+
|
|
1002
|
+
public interface IDateTimeProvider { DateTime UtcNow(); }
|
|
1003
|
+
public class DateTimeProvider : IDateTimeProvider { ... }
|
|
1004
|
+
|
|
1005
|
+
// Result: 40 interfaces for 40 classes, 80 files to navigate
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
**Why developers do it:**
|
|
1009
|
+
Dependency injection frameworks and unit testing guidance recommend coding to interfaces. This gets cargo-culted into "every class needs an interface." The `I` prefix for interfaces is a C# convention that gets applied universally.
|
|
1010
|
+
|
|
1011
|
+
**What goes wrong:**
|
|
1012
|
+
The codebase doubles in file count with no corresponding increase in flexibility or testability. For the 90% of interfaces with exactly one implementation, the interface adds: one additional file, one additional concept to search, one additional place to maintain method signatures. As the Okta developer blog noted, `IInterface` naming is a code smell that signals over-engineering. The actual benefit — swap implementations for testing — is achievable through other means (virtual methods, subclassing in tests, fakes).
|
|
1013
|
+
|
|
1014
|
+
**The fix:**
|
|
1015
|
+
Extract interfaces when: (a) there are or will imminently be multiple implementations, (b) the interface crosses an architectural boundary (e.g., hexagonal ports), or (c) the interface is needed to break a circular dependency. Not: "for every class, automatically."
|
|
1016
|
+
|
|
1017
|
+
```csharp
|
|
1018
|
+
// Before: IUserService exists solely so UserService can be mocked in tests
|
|
1019
|
+
|
|
1020
|
+
// After: make UserService methods virtual, subclass in tests
|
|
1021
|
+
public class UserService {
|
|
1022
|
+
public virtual User GetUser(int id) { ... }
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// In tests
|
|
1026
|
+
public class TestUserService : UserService {
|
|
1027
|
+
public override User GetUser(int id) => _testUsers[id];
|
|
1028
|
+
}
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
**Detection rule:**
|
|
1032
|
+
Flag `I`-prefixed interfaces (or interfaces following `*Interface`, `*Contract` naming) where: (a) only one non-test class implements the interface, and (b) the interface and its sole implementation were created in the same commit.
|
|
1033
|
+
|
|
1034
|
+
---
|
|
1035
|
+
|
|
1036
|
+
## Root Cause Analysis
|
|
1037
|
+
|
|
1038
|
+
| Root Cause | Anti-Patterns Triggered | Primary Driver |
|
|
1039
|
+
|---|---|---|
|
|
1040
|
+
| Naming deferred during flow state | AP-1, AP-17 | Productivity pressure |
|
|
1041
|
+
| Feature evolution without rename discipline | AP-2, AP-18 | Refactoring cost perception |
|
|
1042
|
+
| Legacy conventions carried forward | AP-4, AP-16 | Institutional inertia |
|
|
1043
|
+
| DRY applied as rule rather than heuristic | AP-7, AP-8 | Misapplied principles |
|
|
1044
|
+
| Abstraction defined before second use case | AP-8, AP-19, AP-20 | Speculative design |
|
|
1045
|
+
| No domain vocabulary documented | AP-6, AP-13 | Missing architecture discipline |
|
|
1046
|
+
| Utility-first organization | AP-12 | Missing domain model |
|
|
1047
|
+
| Catch-all verbs used as placeholders | AP-14 | Insufficient domain understanding |
|
|
1048
|
+
| Negative-first reasoning about state | AP-15 | Incremental feature accretion |
|
|
1049
|
+
| Abbreviation culture / line-length habits | AP-3 | Historical constraints misapplied |
|
|
1050
|
+
| Abstraction boundary not designed | AP-9, AP-10, AP-11 | Incomplete architecture definition |
|
|
1051
|
+
| Interface-for-testability cargo cult | AP-20 | Overgeneralized testing guidance |
|
|
1052
|
+
|
|
1053
|
+
---
|
|
1054
|
+
|
|
1055
|
+
## Self-Check Questions
|
|
1056
|
+
|
|
1057
|
+
Use these questions during code review or when auditing a codebase for naming and abstraction quality.
|
|
1058
|
+
|
|
1059
|
+
1. If you removed all comments from a function, could a new team member still understand what it does from names alone? (AP-1, AP-17)
|
|
1060
|
+
|
|
1061
|
+
2. For every boolean field or parameter, does its name start with `is`, `has`, `should`, `can`, `was`, or `will`? (AP-5, AP-15)
|
|
1062
|
+
|
|
1063
|
+
3. Does your codebase have exactly one term for each domain concept, used consistently across all layers? (AP-6)
|
|
1064
|
+
|
|
1065
|
+
4. For every abstraction (class, interface, function), can you name the second use case it serves — not one you imagine in the future, but one that exists today? (AP-8, AP-19, AP-20)
|
|
1066
|
+
|
|
1067
|
+
5. Can every layer in your call stack be removed without the remaining layers needing to know about the removed layer's implementation details? (AP-9, AP-11)
|
|
1068
|
+
|
|
1069
|
+
6. If you renamed every function by its implementation details, would the names change? If yes, the current names may be leaking implementation. (AP-13)
|
|
1070
|
+
|
|
1071
|
+
7. Do you have any files named `utils`, `helpers`, `common`, or `misc` with more than 10 functions? If so, which specific domain does each function belong to? (AP-12)
|
|
1072
|
+
|
|
1073
|
+
8. For every abstract class and interface, how many concrete implementations exist today? If the answer is one, why does the abstraction exist now? (AP-8, AP-19, AP-20)
|
|
1074
|
+
|
|
1075
|
+
9. Are there any functions in your codebase whose sole body is a call to another function with the same parameters? (AP-18)
|
|
1076
|
+
|
|
1077
|
+
10. Are there any functions with more than 5 boolean parameters, or any function whose behavior changes based on a `type` or `mode` parameter? (AP-7)
|
|
1078
|
+
|
|
1079
|
+
11. Does every function name include at least one domain-specific noun alongside its verb? `processOrder` has a noun; `processData` does not. (AP-14)
|
|
1080
|
+
|
|
1081
|
+
12. When you search the codebase for a domain concept (e.g., "user"), do you find it under one name or under many (`user`, `account`, `member`, `profile`)? (AP-6)
|
|
1082
|
+
|
|
1083
|
+
13. Are there places where a higher-level layer fetches all records and filters them in memory when a lower-level query could do it? (AP-10)
|
|
1084
|
+
|
|
1085
|
+
14. For every variable with a comment explaining it, ask: could the comment be eliminated by putting its content in the variable name? (AP-17)
|
|
1086
|
+
|
|
1087
|
+
15. Does your codebase have abbreviations that are not defined in a glossary? Would a developer from another team know what `OrdProc` or `CustMgr` means on day one? (AP-3)
|
|
1088
|
+
|
|
1089
|
+
---
|
|
1090
|
+
|
|
1091
|
+
## Code Smell Quick Reference
|
|
1092
|
+
|
|
1093
|
+
| Smell | Anti-Pattern | One-Line Rule |
|
|
1094
|
+
|---|---|---|
|
|
1095
|
+
| Variable named `data`, `info`, `temp`, `x` | AP-1 | Every name must answer: "data about what?" |
|
|
1096
|
+
| Function description contradicts its behavior | AP-2 | Read the code, not the name — they should match |
|
|
1097
|
+
| Identifier with `Mgr`, `Svc`, `Proc` suffix | AP-3 | Spell it out; abbreviations are not universal |
|
|
1098
|
+
| Variable prefix `str`, `n`, `b`, `arr` | AP-4 | Remove type prefixes in typed languages |
|
|
1099
|
+
| Boolean field named `active`, `delete`, `login` | AP-5 | Prefix with `is_`, `has_`, `should_` |
|
|
1100
|
+
| Same entity called by 3+ names | AP-6 | One concept, one name; document in a glossary |
|
|
1101
|
+
| Function with 6+ flag parameters | AP-7 | Split into focused functions per use case |
|
|
1102
|
+
| Interface with one implementation, created together | AP-8, AP-20 | Build it when the second use case arrives |
|
|
1103
|
+
| SQL string in a service class | AP-9 | Data access details belong in the data layer |
|
|
1104
|
+
| `GetAll()` + in-memory filter in service | AP-10 | Push filtering to the query layer |
|
|
1105
|
+
| 4+ pass-through wrappers in a call chain | AP-11 | Layers should transform, not just relay |
|
|
1106
|
+
| File named `utils.py` over 200 lines | AP-12 | Every function belongs to a domain |
|
|
1107
|
+
| Method named `getOrdersFromDatabase` | AP-13 | Name the intent; hide the mechanism |
|
|
1108
|
+
| Function named `handleData` or `processRequest` | AP-14 | Add a domain noun: `handlePaymentData` |
|
|
1109
|
+
| Boolean named `isNotDisabled`, `cannot_edit` | AP-15 | Flip to positive: `isEnabled`, `can_edit` |
|
|
1110
|
+
| Variable named `user_list`, `config_dict` | AP-16 | Remove type suffix; use type annotation instead |
|
|
1111
|
+
| Line comment immediately above a function | AP-17 | Move the comment content into the function name |
|
|
1112
|
+
| Two functions that do the same thing | AP-18 | Complete renames atomically; delete the alias |
|
|
1113
|
+
| Abstract class with one concrete subclass | AP-19 | Inline to concrete; extract when second arrives |
|
|
1114
|
+
| `I`-prefixed interface, one implementation | AP-20 | Reserve interfaces for real polymorphism |
|
|
1115
|
+
|
|
1116
|
+
---
|
|
1117
|
+
|
|
1118
|
+
*Researched: 2026-03-08 | Sources: [The Wrong Abstraction — Sandi Metz](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction), [The Law of Leaky Abstractions — Joel on Software](https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/), [Abstraction Inversion — Wikipedia](https://en.wikipedia.org/wiki/Abstraction_inversion), [Hungarian Notation Postmortem — SubMain](https://blog.submain.com/hungarian-notation-postmortem-went-wrong/), [IInterface Considered Harmful — Okta Developer](https://developer.okta.com/blog/2019/06/25/iinterface-considered-harmful), [Dunghill Anti-Pattern — Matti Lehtinen](https://mattilehtinen.com/articles/dunghill-anti-pattern-why-utility-classes-and-modules-smell/), [Post-Architecture: Premature Abstraction — Arend Jr](https://www.arendjr.nl/blog/2024/07/post-architecture-premature-abstraction-is-the-root-of-all-evil/), [AHA Programming — Kent C. Dodds](https://kentcdodds.com/blog/aha-programming), [Indirection Is Not Abstraction — Silas Reinagel](https://www.silasreinagel.com/blog/2018/10/30/indirection-is-not-abstraction/)*
|