@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,1186 @@
|
|
|
1
|
+
# Code Smells
|
|
2
|
+
|
|
3
|
+
> Code smells are surface-level indicators of deeper structural problems. They are not bugs -- the code compiles, the tests pass -- but they signal design weaknesses that compound over time, slow development, and breed defects. Martin Fowler and Kent Beck cataloged them in *Refactoring* (1999); the list here extends and modernizes that catalog with real-world incidents.
|
|
4
|
+
|
|
5
|
+
> **Domain:** Code
|
|
6
|
+
> **Anti-patterns covered:** 20
|
|
7
|
+
> **Highest severity:** Critical
|
|
8
|
+
|
|
9
|
+
## Anti-Patterns
|
|
10
|
+
|
|
11
|
+
### AP-01: God Class / God Object
|
|
12
|
+
|
|
13
|
+
**Also known as:** Blob, Monster Class, Kitchen Sink
|
|
14
|
+
**Frequency:** Very Common
|
|
15
|
+
**Severity:** Critical
|
|
16
|
+
**Detection difficulty:** Easy
|
|
17
|
+
|
|
18
|
+
**What it looks like:**
|
|
19
|
+
|
|
20
|
+
A single class that centralizes most of the application logic, accumulating hundreds of methods and dozens of fields across unrelated responsibilities.
|
|
21
|
+
|
|
22
|
+
```java
|
|
23
|
+
class ApplicationManager {
|
|
24
|
+
// 143 methods, 31 attributes
|
|
25
|
+
void handleLogin() { ... }
|
|
26
|
+
void calculateTax() { ... }
|
|
27
|
+
void sendEmail() { ... }
|
|
28
|
+
void renderDashboard() { ... }
|
|
29
|
+
void processPayment() { ... }
|
|
30
|
+
void generateReport() { ... }
|
|
31
|
+
// ... 137 more methods
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Why developers do it:**
|
|
36
|
+
|
|
37
|
+
Sprint pressure and approaching deadlines cause developers to pack new functionality into whatever class is already "doing things." The god class grows incrementally -- no single commit looks unreasonable, but the cumulative result is catastrophic.
|
|
38
|
+
|
|
39
|
+
**What goes wrong:**
|
|
40
|
+
|
|
41
|
+
The jEdit text editor's `JEdit` class grew to 143 methods and 31 attributes accessing 28 attributes from 13 different classes, becoming nearly impossible to modify without regression. In any god class, a change to one responsibility (e.g., email formatting) can silently break unrelated functionality (e.g., tax calculation) because they share mutable state. Merge conflicts become constant as every developer touches the same file.
|
|
42
|
+
|
|
43
|
+
**The fix:**
|
|
44
|
+
|
|
45
|
+
Extract classes by responsibility using the Single Responsibility Principle.
|
|
46
|
+
|
|
47
|
+
```java
|
|
48
|
+
// Before: one god class
|
|
49
|
+
class ApplicationManager { /* everything */ }
|
|
50
|
+
|
|
51
|
+
// After: cohesive, focused classes
|
|
52
|
+
class AuthenticationService { void handleLogin() { ... } }
|
|
53
|
+
class TaxCalculator { BigDecimal calculateTax(Order o) { ... } }
|
|
54
|
+
class EmailService { void sendEmail(Message m) { ... } }
|
|
55
|
+
class DashboardRenderer { void render(DashboardModel m) { ... } }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Detection rule:**
|
|
59
|
+
|
|
60
|
+
Flag classes with more than 20 public methods OR more than 10 fields OR more than 500 lines of code.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### AP-02: Copy-Paste Programming
|
|
65
|
+
|
|
66
|
+
**Also known as:** Duplicated Code, Clone-and-Own, Shotgun Cloning
|
|
67
|
+
**Frequency:** Very Common
|
|
68
|
+
**Severity:** Critical
|
|
69
|
+
**Detection difficulty:** Moderate
|
|
70
|
+
|
|
71
|
+
**What it looks like:**
|
|
72
|
+
|
|
73
|
+
Identical or near-identical blocks of code appear in multiple locations. When a bug is fixed in one copy, the other copies remain broken.
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
# File: billing.py
|
|
77
|
+
def calculate_total(items):
|
|
78
|
+
total = 0
|
|
79
|
+
for item in items:
|
|
80
|
+
total += item.price * item.quantity
|
|
81
|
+
if item.taxable:
|
|
82
|
+
total += item.price * item.quantity * 0.08
|
|
83
|
+
return total
|
|
84
|
+
|
|
85
|
+
# File: reporting.py (copy-pasted, same logic)
|
|
86
|
+
def get_order_total(items):
|
|
87
|
+
total = 0
|
|
88
|
+
for item in items:
|
|
89
|
+
total += item.price * item.quantity
|
|
90
|
+
if item.taxable:
|
|
91
|
+
total += item.price * item.quantity * 0.08 # tax rate hardcoded
|
|
92
|
+
return total
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Why developers do it:**
|
|
96
|
+
|
|
97
|
+
It feels faster to copy working code than to extract and parameterize a shared function. Developers fear breaking the original by refactoring it.
|
|
98
|
+
|
|
99
|
+
**What goes wrong:**
|
|
100
|
+
|
|
101
|
+
Apple's "goto fail" SSL vulnerability (CVE-2014-1266) is the canonical example. Code was copied from `SSLDecodeSignedServerKeyExchange` to `SSLVerifySignedServerKeyExchange`, and during the copy a duplicate `goto fail;` line was introduced. This single duplicated line meant SSL/TLS certificate verification was silently skipped on every connection in iOS 6.x/7.x and OS X 10.9.x, exposing millions of devices to man-in-the-middle attacks. The fix was deleting one line of code. Research by Li et al. (CP-Miner) found that 20-28% of code in certain Linux kernel modules was copy-pasted, with bugs routinely propagating through clones. When a vulnerability exists in duplicated code, fixing one copy leaves the others exploitable -- a pattern documented in OpenSSL and multiple e-commerce platforms.
|
|
102
|
+
|
|
103
|
+
**The fix:**
|
|
104
|
+
|
|
105
|
+
Extract shared logic into a single function. Use parameterization for variations.
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# Shared module
|
|
109
|
+
def calculate_total(items, tax_rate=0.08):
|
|
110
|
+
total = 0
|
|
111
|
+
for item in items:
|
|
112
|
+
subtotal = item.price * item.quantity
|
|
113
|
+
total += subtotal
|
|
114
|
+
if item.taxable:
|
|
115
|
+
total += subtotal * tax_rate
|
|
116
|
+
return total
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Detection rule:**
|
|
120
|
+
|
|
121
|
+
Flag code blocks of 6+ lines that are identical or differ only in variable names. Tools: PMD CPD, jscpd, SonarQube duplication detection.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
### AP-03: Primitive Obsession
|
|
126
|
+
|
|
127
|
+
**Also known as:** Stringly Typed, Weakly Modeled Domain
|
|
128
|
+
**Frequency:** Very Common
|
|
129
|
+
**Severity:** Critical
|
|
130
|
+
**Detection difficulty:** Hard
|
|
131
|
+
|
|
132
|
+
**What it looks like:**
|
|
133
|
+
|
|
134
|
+
Using primitive types (int, string, double) to represent domain concepts that deserve their own types, losing all semantic safety.
|
|
135
|
+
|
|
136
|
+
```java
|
|
137
|
+
// Dangerous: both are just doubles
|
|
138
|
+
double thrustNewtons = computeThrust();
|
|
139
|
+
double thrustPoundForce = sensor.readThrust();
|
|
140
|
+
|
|
141
|
+
// Nothing prevents mixing them
|
|
142
|
+
double totalThrust = thrustNewtons + thrustPoundForce; // unit mismatch!
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Why developers do it:**
|
|
146
|
+
|
|
147
|
+
Creating wrapper types feels like overengineering. Primitives are easy, familiar, and require no extra classes. The type mismatch risk feels theoretical -- until it is not.
|
|
148
|
+
|
|
149
|
+
**What goes wrong:**
|
|
150
|
+
|
|
151
|
+
The Mars Climate Orbiter ($327.6 million) was destroyed on September 23, 1999, because Lockheed Martin's ground software produced thrust values in pound-force seconds while NASA's navigation software expected Newton seconds. Both systems used raw `double` values with no type-level unit distinction. A typed wrapper (e.g., `Force<Newtons>` vs. `Force<PoundForce>`) would have caught the mismatch at compile time. The Ariane 5 explosion (1996, ~$370 million) had a related cause: a 64-bit floating-point value representing horizontal velocity was converted to a 16-bit signed integer without bounds checking. The value exceeded 32,767, causing an overflow that crashed the inertial reference system 40 seconds after launch.
|
|
152
|
+
|
|
153
|
+
**The fix:**
|
|
154
|
+
|
|
155
|
+
Introduce value objects or wrapper types for domain concepts.
|
|
156
|
+
|
|
157
|
+
```java
|
|
158
|
+
// After: units are enforced by the type system
|
|
159
|
+
record Newtons(double value) {}
|
|
160
|
+
record PoundForce(double value) {}
|
|
161
|
+
|
|
162
|
+
Newtons toNewtons(PoundForce pf) {
|
|
163
|
+
return new Newtons(pf.value() * 4.44822);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Compiler rejects: addThrust(Newtons, PoundForce) -- type mismatch
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Detection rule:**
|
|
170
|
+
|
|
171
|
+
Flag methods with more than 2 parameters of the same primitive type. Flag `String` parameters named `*id`, `*code`, `*type`, `*status`, `*currency`.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
### AP-04: Long Method / Long Function
|
|
176
|
+
|
|
177
|
+
**Also known as:** Mega-Function, Do-Everything Method
|
|
178
|
+
**Frequency:** Very Common
|
|
179
|
+
**Severity:** High
|
|
180
|
+
**Detection difficulty:** Easy
|
|
181
|
+
|
|
182
|
+
**What it looks like:**
|
|
183
|
+
|
|
184
|
+
A single function that spans hundreds of lines, mixing multiple levels of abstraction and responsibilities.
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
def process_order(order):
|
|
188
|
+
# Validate (lines 1-45)
|
|
189
|
+
# Apply discounts (lines 46-90)
|
|
190
|
+
# Calculate tax (lines 91-130)
|
|
191
|
+
# Check inventory (lines 131-180)
|
|
192
|
+
# Process payment (lines 181-240)
|
|
193
|
+
# Send confirmation email (lines 241-290)
|
|
194
|
+
# Update analytics (lines 291-340)
|
|
195
|
+
# ... total: 340 lines
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Why developers do it:**
|
|
199
|
+
|
|
200
|
+
Adding a few lines to an existing function is the path of least resistance. Extracting methods feels like unnecessary ceremony, especially under deadline pressure.
|
|
201
|
+
|
|
202
|
+
**What goes wrong:**
|
|
203
|
+
|
|
204
|
+
Long methods are the primary driver of high cyclomatic complexity. Research by SonarSource shows that functions exceeding cognitive complexity thresholds are disproportionately associated with bug-introducing commits. Each additional responsibility interleaved in a long method multiplies the number of states a developer must hold in working memory. In practice, developers modifying a 300-line function routinely introduce bugs in logic paths they did not intend to touch, because they cannot see all the interacting state.
|
|
205
|
+
|
|
206
|
+
**The fix:**
|
|
207
|
+
|
|
208
|
+
Extract method by responsibility. Each extracted method should operate at one level of abstraction.
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
def process_order(order):
|
|
212
|
+
validated = validate_order(order)
|
|
213
|
+
discounted = apply_discounts(validated)
|
|
214
|
+
taxed = calculate_tax(discounted)
|
|
215
|
+
check_inventory(taxed)
|
|
216
|
+
charge = process_payment(taxed)
|
|
217
|
+
send_confirmation(order, charge)
|
|
218
|
+
update_analytics(order)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Detection rule:**
|
|
222
|
+
|
|
223
|
+
Flag functions exceeding 30 lines of logic (excluding blank lines and comments). Flag cyclomatic complexity > 10.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
### AP-05: Deep Nesting / Arrow Code
|
|
228
|
+
|
|
229
|
+
**Also known as:** Pyramid of Doom, Arrowhead Anti-Pattern, Callback Hell
|
|
230
|
+
**Frequency:** Very Common
|
|
231
|
+
**Severity:** High
|
|
232
|
+
**Detection difficulty:** Easy
|
|
233
|
+
|
|
234
|
+
**What it looks like:**
|
|
235
|
+
|
|
236
|
+
Control flow nested 4+ levels deep, forming an arrow shape when the code is viewed sideways.
|
|
237
|
+
|
|
238
|
+
```javascript
|
|
239
|
+
function processRequest(req) {
|
|
240
|
+
if (req) {
|
|
241
|
+
if (req.user) {
|
|
242
|
+
if (req.user.isActive) {
|
|
243
|
+
if (req.body) {
|
|
244
|
+
if (req.body.items) {
|
|
245
|
+
if (req.body.items.length > 0) {
|
|
246
|
+
// actual logic buried here
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Why developers do it:**
|
|
257
|
+
|
|
258
|
+
Each additional condition feels like a small, reasonable guard. The nesting accumulates one `if` at a time across multiple commits.
|
|
259
|
+
|
|
260
|
+
**What goes wrong:**
|
|
261
|
+
|
|
262
|
+
Code beyond 3 levels of nesting has high cognitive complexity, forcing developers to track multiple conditions simultaneously. Research from TU Delft shows this directly correlates with increased bug density. Bugs hide in obscure branches because reviewers cannot hold all the conditional paths in working memory. In async code (Node.js callbacks, nested promises), this pattern creates "callback hell" where error handling paths become untraceable.
|
|
263
|
+
|
|
264
|
+
**The fix:**
|
|
265
|
+
|
|
266
|
+
Use early returns (guard clauses) to flatten the nesting.
|
|
267
|
+
|
|
268
|
+
```javascript
|
|
269
|
+
function processRequest(req) {
|
|
270
|
+
if (!req?.user?.isActive) return;
|
|
271
|
+
if (!req.body?.items?.length) return;
|
|
272
|
+
|
|
273
|
+
// actual logic at top level
|
|
274
|
+
processItems(req.body.items);
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**Detection rule:**
|
|
279
|
+
|
|
280
|
+
Flag any code block nested more than 3 levels deep. Flag cognitive complexity > 15 per function.
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
### AP-06: Feature Envy
|
|
285
|
+
|
|
286
|
+
**Also known as:** Misplaced Method, Data Jealousy
|
|
287
|
+
**Frequency:** Common
|
|
288
|
+
**Severity:** High
|
|
289
|
+
**Detection difficulty:** Moderate
|
|
290
|
+
|
|
291
|
+
**What it looks like:**
|
|
292
|
+
|
|
293
|
+
A method that uses more data and methods from another class than from its own.
|
|
294
|
+
|
|
295
|
+
```java
|
|
296
|
+
class OrderPrinter {
|
|
297
|
+
String formatOrder(Order order) {
|
|
298
|
+
return order.getCustomer().getName() + "\n"
|
|
299
|
+
+ order.getCustomer().getAddress().getStreet() + "\n"
|
|
300
|
+
+ order.getCustomer().getAddress().getCity() + ", "
|
|
301
|
+
+ order.getCustomer().getAddress().getState() + " "
|
|
302
|
+
+ order.getCustomer().getAddress().getZip() + "\n"
|
|
303
|
+
+ "Total: $" + order.getTotal();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**Why developers do it:**
|
|
309
|
+
|
|
310
|
+
The developer has the data available through getters and builds logic where they happen to be working, rather than putting the method where the data lives.
|
|
311
|
+
|
|
312
|
+
**What goes wrong:**
|
|
313
|
+
|
|
314
|
+
Feature envy binds classes together tightly. When `Customer` or `Address` changes its internal representation, `OrderPrinter` breaks -- even though `OrderPrinter` has no business knowing the internal structure of addresses. This coupling is a leading cause of shotgun surgery (AP-10). Research shows feature-envious methods are involved in bug-introducing commits at a significantly higher rate than well-placed methods, because changes to the envied class propagate unpredictably.
|
|
315
|
+
|
|
316
|
+
**The fix:**
|
|
317
|
+
|
|
318
|
+
Move the method to the class whose data it uses.
|
|
319
|
+
|
|
320
|
+
```java
|
|
321
|
+
class Customer {
|
|
322
|
+
String formatAddress() {
|
|
323
|
+
return name + "\n" + address.format();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
class Address {
|
|
328
|
+
String format() {
|
|
329
|
+
return street + "\n" + city + ", " + state + " " + zip;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Detection rule:**
|
|
335
|
+
|
|
336
|
+
Flag methods where more than 50% of accessed fields/methods belong to a different class. Tools: IntelliJ IDEA structural search, JDeodorant.
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
### AP-07: Data Clumps
|
|
341
|
+
|
|
342
|
+
**Also known as:** Parameter Clusters, Traveling Data
|
|
343
|
+
**Frequency:** Common
|
|
344
|
+
**Severity:** Medium
|
|
345
|
+
**Detection difficulty:** Moderate
|
|
346
|
+
|
|
347
|
+
**What it looks like:**
|
|
348
|
+
|
|
349
|
+
The same group of parameters appears together across multiple method signatures.
|
|
350
|
+
|
|
351
|
+
```python
|
|
352
|
+
def create_user(first_name, last_name, street, city, state, zip_code):
|
|
353
|
+
...
|
|
354
|
+
|
|
355
|
+
def update_address(user_id, street, city, state, zip_code):
|
|
356
|
+
...
|
|
357
|
+
|
|
358
|
+
def validate_shipping(street, city, state, zip_code):
|
|
359
|
+
...
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
**Why developers do it:**
|
|
363
|
+
|
|
364
|
+
Each parameter is "just one more." The clump forms incrementally, and introducing a new class for a group of parameters feels heavyweight early on.
|
|
365
|
+
|
|
366
|
+
**What goes wrong:**
|
|
367
|
+
|
|
368
|
+
Data clumps force every caller to know the exact order and types of parameters. When a new field is needed (e.g., `country`), every function signature and every call site must change -- a combinatorial maintenance burden. Martin Fowler notes that data clumps are often primitive values that nobody thinks to turn into an object, leading to duplicated validation logic scattered across every function that receives the clump.
|
|
369
|
+
|
|
370
|
+
**The fix:**
|
|
371
|
+
|
|
372
|
+
Extract a parameter object.
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
@dataclass
|
|
376
|
+
class Address:
|
|
377
|
+
street: str
|
|
378
|
+
city: str
|
|
379
|
+
state: str
|
|
380
|
+
zip_code: str
|
|
381
|
+
|
|
382
|
+
def create_user(first_name: str, last_name: str, address: Address):
|
|
383
|
+
...
|
|
384
|
+
|
|
385
|
+
def update_address(user_id: int, address: Address):
|
|
386
|
+
...
|
|
387
|
+
|
|
388
|
+
def validate_shipping(address: Address):
|
|
389
|
+
...
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Detection rule:**
|
|
393
|
+
|
|
394
|
+
Flag 3+ parameters that co-occur in 3+ method signatures. Flag any method with more than 4 parameters.
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
### AP-08: Magic Numbers and Strings
|
|
399
|
+
|
|
400
|
+
**Also known as:** Unnamed Constants, Hard-Coded Values
|
|
401
|
+
**Frequency:** Very Common
|
|
402
|
+
**Severity:** Medium
|
|
403
|
+
**Detection difficulty:** Easy
|
|
404
|
+
|
|
405
|
+
**What it looks like:**
|
|
406
|
+
|
|
407
|
+
Literal values embedded directly in logic with no explanation of what they represent.
|
|
408
|
+
|
|
409
|
+
```python
|
|
410
|
+
def calculate_shipping(weight):
|
|
411
|
+
if weight > 50:
|
|
412
|
+
return weight * 0.45 + 8.99
|
|
413
|
+
elif weight > 20:
|
|
414
|
+
return weight * 0.35 + 4.99
|
|
415
|
+
else:
|
|
416
|
+
return 3.99
|
|
417
|
+
|
|
418
|
+
if user.role == "ADMN": # typo? or valid code?
|
|
419
|
+
grant_access()
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
**Why developers do it:**
|
|
423
|
+
|
|
424
|
+
The meaning is obvious to the person writing it. Adding named constants feels verbose when "everyone knows" what 0.45 means.
|
|
425
|
+
|
|
426
|
+
**What goes wrong:**
|
|
427
|
+
|
|
428
|
+
When the shipping rate changes, a developer must find every occurrence of `0.45` across the codebase -- and distinguish it from other uses of `0.45` (tax rate? discount factor?). The string `"ADMN"` is a typo waiting to happen with no compiler protection. In the Ariane 5 failure, hardcoded assumptions about maximum horizontal velocity values (appropriate for Ariane 4 but not Ariane 5) were embedded directly in the inertial reference system code rather than being parameterized, contributing to the overflow that destroyed the rocket.
|
|
429
|
+
|
|
430
|
+
**The fix:**
|
|
431
|
+
|
|
432
|
+
Extract named constants. Use enums for categorical values.
|
|
433
|
+
|
|
434
|
+
```python
|
|
435
|
+
HEAVY_WEIGHT_THRESHOLD = 50
|
|
436
|
+
MEDIUM_WEIGHT_THRESHOLD = 20
|
|
437
|
+
HEAVY_RATE_PER_KG = 0.45
|
|
438
|
+
HEAVY_BASE_FEE = 8.99
|
|
439
|
+
MEDIUM_RATE_PER_KG = 0.35
|
|
440
|
+
MEDIUM_BASE_FEE = 4.99
|
|
441
|
+
LIGHT_FLAT_FEE = 3.99
|
|
442
|
+
|
|
443
|
+
class Role(Enum):
|
|
444
|
+
ADMIN = "ADMIN"
|
|
445
|
+
USER = "USER"
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
**Detection rule:**
|
|
449
|
+
|
|
450
|
+
Flag numeric literals other than 0, 1, -1 in logic code (not declarations). Flag string comparisons used for branching.
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
### AP-09: Dead Code
|
|
455
|
+
|
|
456
|
+
**Also known as:** Zombie Code, Unreachable Code, Vestigial Code
|
|
457
|
+
**Frequency:** Common
|
|
458
|
+
**Severity:** Critical
|
|
459
|
+
**Detection difficulty:** Moderate
|
|
460
|
+
|
|
461
|
+
**What it looks like:**
|
|
462
|
+
|
|
463
|
+
Code that is never executed: unreachable branches, unused functions, commented-out blocks, obsolete feature flags.
|
|
464
|
+
|
|
465
|
+
```java
|
|
466
|
+
// Deprecated in 2003, never removed
|
|
467
|
+
void executePowerPeg(Order order) {
|
|
468
|
+
// Legacy market-making algorithm
|
|
469
|
+
while (order.isOpen()) {
|
|
470
|
+
placeAggressiveTrade(order);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**Why developers do it:**
|
|
476
|
+
|
|
477
|
+
"We might need it later." Deleting code feels risky. Commented-out code serves as a "backup." Feature flags are left in place long after the feature ships.
|
|
478
|
+
|
|
479
|
+
**What goes wrong:**
|
|
480
|
+
|
|
481
|
+
Knight Capital Group, August 1, 2012: During deployment of a new trading system (RLP), only 7 of 8 servers received the update. The 8th server still contained dormant "Power Peg" code from 2003 that was never fully removed. A reused configuration flag accidentally reactivated Power Peg, which began executing an obsolete aggressive market-making strategy. In 45 minutes, the algorithm acquired $7.65 billion in unwanted positions across 154 stocks, resulting in a $440 million loss. Knight Capital's stock dropped 75%, and the company was forced to sell itself. The root cause was dead code that should have been deleted a decade earlier.
|
|
482
|
+
|
|
483
|
+
**The fix:**
|
|
484
|
+
|
|
485
|
+
Delete dead code. Version control is your backup. Remove obsolete feature flags. Use time-bounded feature flags with automatic expiration.
|
|
486
|
+
|
|
487
|
+
```java
|
|
488
|
+
// Before: dead code left in place "just in case"
|
|
489
|
+
// void executePowerPeg(Order order) { ... }
|
|
490
|
+
|
|
491
|
+
// After: deleted entirely.
|
|
492
|
+
// If needed, recover from git history: git log --all -p -- PowerPeg.java
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**Detection rule:**
|
|
496
|
+
|
|
497
|
+
Flag functions with zero callers. Flag commented-out code blocks > 3 lines. Flag feature flags older than 90 days. Tools: IntelliJ "unused declaration," ESLint no-unreachable, SonarQube dead code detection.
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
### AP-10: Shotgun Surgery
|
|
502
|
+
|
|
503
|
+
**Also known as:** Scattered Change, Ripple Effect
|
|
504
|
+
**Frequency:** Common
|
|
505
|
+
**Severity:** High
|
|
506
|
+
**Detection difficulty:** Hard
|
|
507
|
+
|
|
508
|
+
**What it looks like:**
|
|
509
|
+
|
|
510
|
+
A single logical change requires modifications across many files and classes.
|
|
511
|
+
|
|
512
|
+
```
|
|
513
|
+
Adding a new "currency" field requires changes in:
|
|
514
|
+
- UserModel.java (add field)
|
|
515
|
+
- UserDTO.java (add field)
|
|
516
|
+
- UserMapper.java (add mapping)
|
|
517
|
+
- UserValidator.java (add validation)
|
|
518
|
+
- UserRepository.java (update query)
|
|
519
|
+
- UserController.java (add parameter)
|
|
520
|
+
- UserService.java (pass through)
|
|
521
|
+
- user-form.html (add input)
|
|
522
|
+
- user-api.yaml (update spec)
|
|
523
|
+
- UserTest.java (update fixtures)
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
**Why developers do it:**
|
|
527
|
+
|
|
528
|
+
It emerges from poorly factored abstractions, often due to copy-paste (AP-02) or feature envy (AP-06). Each individual class looks reasonable in isolation; the problem is only visible when making a cross-cutting change.
|
|
529
|
+
|
|
530
|
+
**What goes wrong:**
|
|
531
|
+
|
|
532
|
+
Shotgun surgery is the inverse of divergent change (AP-11). Every change becomes a multi-file commit with high risk of missing one location. Wikipedia notes that due to commercial pressure, developers often resort to copy-paste rather than refactoring, which compounds the shotgun surgery pattern. Missing one file in the change set produces silent bugs -- the system compiles, tests may pass, but one code path uses stale logic.
|
|
533
|
+
|
|
534
|
+
**The fix:**
|
|
535
|
+
|
|
536
|
+
Consolidate related logic using Move Method and Move Field. If multiple classes change together for every feature, they likely belong in one module.
|
|
537
|
+
|
|
538
|
+
```java
|
|
539
|
+
// Before: currency scattered across 10 files
|
|
540
|
+
// After: currency encapsulated in a Money value object
|
|
541
|
+
record Money(BigDecimal amount, Currency currency) {
|
|
542
|
+
// All currency logic in one place
|
|
543
|
+
Money convertTo(Currency target, ExchangeRate rate) { ... }
|
|
544
|
+
String format() { ... }
|
|
545
|
+
}
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
**Detection rule:**
|
|
549
|
+
|
|
550
|
+
Flag commits that modify more than 5 files for a single logical change. Track co-change frequency in version history. Tools: CodeScene temporal coupling analysis.
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
### AP-11: Divergent Change
|
|
555
|
+
|
|
556
|
+
**Also known as:** Swiss Army Class, Multiple Reasons to Change
|
|
557
|
+
**Frequency:** Common
|
|
558
|
+
**Severity:** High
|
|
559
|
+
**Detection difficulty:** Moderate
|
|
560
|
+
|
|
561
|
+
**What it looks like:**
|
|
562
|
+
|
|
563
|
+
A single class is modified for many different, unrelated reasons. This is the sibling of shotgun surgery, but in the opposite direction: instead of one change touching many classes, many different changes touch one class.
|
|
564
|
+
|
|
565
|
+
```python
|
|
566
|
+
class ReportGenerator:
|
|
567
|
+
def generate_pdf(self, data): ... # changed for PDF library updates
|
|
568
|
+
def fetch_from_database(self, q): ... # changed for schema migrations
|
|
569
|
+
def apply_business_rules(self, d): ... # changed for policy updates
|
|
570
|
+
def send_email(self, report): ... # changed for email provider switches
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
**Why developers do it:**
|
|
574
|
+
|
|
575
|
+
The class started with one responsibility and accumulated others because "it already has the data" or "it's the natural place." Each addition seemed small.
|
|
576
|
+
|
|
577
|
+
**What goes wrong:**
|
|
578
|
+
|
|
579
|
+
Every team member working on a different concern (PDF rendering, database, email) must modify the same file, causing constant merge conflicts and increasing the risk that a database schema change accidentally breaks email sending. This is the god class (AP-01) in its early growth phase.
|
|
580
|
+
|
|
581
|
+
**The fix:**
|
|
582
|
+
|
|
583
|
+
Extract classes by axis of change.
|
|
584
|
+
|
|
585
|
+
```python
|
|
586
|
+
class DataFetcher:
|
|
587
|
+
def fetch(self, query): ...
|
|
588
|
+
|
|
589
|
+
class BusinessRuleEngine:
|
|
590
|
+
def apply(self, data): ...
|
|
591
|
+
|
|
592
|
+
class PdfRenderer:
|
|
593
|
+
def render(self, report): ...
|
|
594
|
+
|
|
595
|
+
class ReportEmailer:
|
|
596
|
+
def send(self, report): ...
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
**Detection rule:**
|
|
600
|
+
|
|
601
|
+
Flag classes modified in more than 5 unrelated pull requests within 30 days. Track distinct commit message topics per file.
|
|
602
|
+
|
|
603
|
+
---
|
|
604
|
+
|
|
605
|
+
### AP-12: Speculative Generality
|
|
606
|
+
|
|
607
|
+
**Also known as:** YAGNI Violation, Premature Abstraction, Crystal Ball Coding
|
|
608
|
+
**Frequency:** Common
|
|
609
|
+
**Severity:** Medium
|
|
610
|
+
**Detection difficulty:** Moderate
|
|
611
|
+
|
|
612
|
+
**What it looks like:**
|
|
613
|
+
|
|
614
|
+
Abstractions, interfaces, and extension points built for hypothetical future requirements that never materialize.
|
|
615
|
+
|
|
616
|
+
```java
|
|
617
|
+
// Interface with exactly one implementation
|
|
618
|
+
interface DataProcessor<T extends Serializable & Comparable<T>> {
|
|
619
|
+
ProcessResult<T> process(ProcessContext<T> ctx, ProcessOptions opts);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Abstract factory for the one processor that exists
|
|
623
|
+
abstract class DataProcessorFactory<T extends Serializable & Comparable<T>> {
|
|
624
|
+
abstract DataProcessor<T> create(FactoryConfig config);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// The only concrete implementation
|
|
628
|
+
class CsvDataProcessor implements DataProcessor<String> {
|
|
629
|
+
// ... the actual logic
|
|
630
|
+
}
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
**Why developers do it:**
|
|
634
|
+
|
|
635
|
+
Anticipating future requirements feels like good engineering. "What if we need to support XML later?" The developer builds the abstraction now to avoid refactoring later.
|
|
636
|
+
|
|
637
|
+
**What goes wrong:**
|
|
638
|
+
|
|
639
|
+
The unused abstractions add cognitive overhead for every developer who reads the code. They must understand the generic type parameters, the factory pattern, and the interface -- all to find the one class that does the actual work. The YAGNI principle (You Aren't Gonna Need It) exists precisely because speculative abstractions are wrong more often than they are right, and the cost of maintaining them exceeds the cost of adding them later when actually needed. Test cases become the only users of speculative classes, which is a direct indicator of this smell.
|
|
640
|
+
|
|
641
|
+
**The fix:**
|
|
642
|
+
|
|
643
|
+
Delete unused abstractions. Add them back when (and only when) a second concrete need arises.
|
|
644
|
+
|
|
645
|
+
```java
|
|
646
|
+
// Before: interface + factory + abstract class + one implementation
|
|
647
|
+
// After: just the implementation
|
|
648
|
+
class CsvDataProcessor {
|
|
649
|
+
ProcessResult process(List<String> records) { ... }
|
|
650
|
+
}
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
**Detection rule:**
|
|
654
|
+
|
|
655
|
+
Flag interfaces with exactly one implementation. Flag abstract classes with exactly one subclass. Flag type parameters used in only one concrete type. Flag classes whose only callers are tests.
|
|
656
|
+
|
|
657
|
+
---
|
|
658
|
+
|
|
659
|
+
### AP-13: Boolean Blindness
|
|
660
|
+
|
|
661
|
+
**Also known as:** Boolean Trap, Flag Arguments
|
|
662
|
+
**Frequency:** Common
|
|
663
|
+
**Severity:** Medium
|
|
664
|
+
**Detection difficulty:** Moderate
|
|
665
|
+
|
|
666
|
+
**What it looks like:**
|
|
667
|
+
|
|
668
|
+
Boolean parameters that obscure meaning at the call site, or boolean return values that erase the semantic information they represent.
|
|
669
|
+
|
|
670
|
+
```python
|
|
671
|
+
# What does True mean here?
|
|
672
|
+
widget.render(True, False, True)
|
|
673
|
+
|
|
674
|
+
# Does this sell alcohol to minors or prevent it?
|
|
675
|
+
sell_alcohol(user, lambda u: u.age >= 21) # returns bool, but what does True mean?
|
|
676
|
+
|
|
677
|
+
# Caller must remember parameter order
|
|
678
|
+
create_user("Alice", True, False, True)
|
|
679
|
+
# ^ ^ ^
|
|
680
|
+
# admin? active? verified?
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
**Why developers do it:**
|
|
684
|
+
|
|
685
|
+
Booleans are the simplest type. Adding a parameter to toggle behavior feels cleaner than creating separate methods or enum types.
|
|
686
|
+
|
|
687
|
+
**What goes wrong:**
|
|
688
|
+
|
|
689
|
+
At every call site, the reader must look up the method signature to understand what `True` and `False` mean. Adam Johnson documented the "boolean trap" in Python type hints, showing how boolean parameters become a persistent source of inverted-logic bugs. When `create_user("Alice", True, False, True)` is called, swapping two adjacent booleans produces a user who is active but not admin -- a silent, type-safe bug that passes all compile-time checks.
|
|
690
|
+
|
|
691
|
+
**The fix:**
|
|
692
|
+
|
|
693
|
+
Replace booleans with enums or named types. Use keyword arguments.
|
|
694
|
+
|
|
695
|
+
```python
|
|
696
|
+
class Permission(Enum):
|
|
697
|
+
ADMIN = "admin"
|
|
698
|
+
USER = "user"
|
|
699
|
+
|
|
700
|
+
class Status(Enum):
|
|
701
|
+
ACTIVE = "active"
|
|
702
|
+
INACTIVE = "inactive"
|
|
703
|
+
|
|
704
|
+
create_user("Alice", permission=Permission.ADMIN,
|
|
705
|
+
status=Status.ACTIVE, verified=True)
|
|
706
|
+
|
|
707
|
+
# Or separate methods
|
|
708
|
+
widget.render_expanded()
|
|
709
|
+
widget.render_collapsed()
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
**Detection rule:**
|
|
713
|
+
|
|
714
|
+
Flag methods with more than 1 boolean parameter. Flag call sites with boolean literals. Flag `def foo(..., flag: bool)` patterns.
|
|
715
|
+
|
|
716
|
+
---
|
|
717
|
+
|
|
718
|
+
### AP-14: Comments as Deodorant
|
|
719
|
+
|
|
720
|
+
**Also known as:** Comment Overcompensation, Explaining Bad Code
|
|
721
|
+
**Frequency:** Very Common
|
|
722
|
+
**Severity:** Medium
|
|
723
|
+
**Detection difficulty:** Easy
|
|
724
|
+
|
|
725
|
+
**What it looks like:**
|
|
726
|
+
|
|
727
|
+
Long comments that explain what confusing code does, instead of rewriting the code to be self-explanatory.
|
|
728
|
+
|
|
729
|
+
```java
|
|
730
|
+
// Check if the user is eligible for discount:
|
|
731
|
+
// The user must be active (status == 1), must have been
|
|
732
|
+
// a member for at least 2 years (joining date before
|
|
733
|
+
// 730 days ago), and must not be on the blacklist
|
|
734
|
+
// (blacklist flag == 0), and their order total must
|
|
735
|
+
// exceed $100
|
|
736
|
+
if (u.s == 1 && daysSince(u.j) > 730 && u.b == 0 && o.t > 100) {
|
|
737
|
+
applyDiscount(o);
|
|
738
|
+
}
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
**Why developers do it:**
|
|
742
|
+
|
|
743
|
+
Writing a comment is faster than refactoring the code. The developer recognizes the code is unclear but addresses the symptom (readability) rather than the cause (poor naming, cryptic logic).
|
|
744
|
+
|
|
745
|
+
**What goes wrong:**
|
|
746
|
+
|
|
747
|
+
Research by Lin et al. (2024) found that code-comment inconsistencies are 1.5x more likely to be involved in bug-introducing commits than consistent changes. Comments drift from code as the code evolves. A documented Mozilla case showed an outdated comment causing a developer to build a feature on assumed behavior that no longer existed, introducing a bug in a later version. The comment above will eventually become wrong when the discount rules change, but the cryptic variable names (`u.s`, `u.j`, `u.b`) will remain opaque forever.
|
|
748
|
+
|
|
749
|
+
**The fix:**
|
|
750
|
+
|
|
751
|
+
Make the code self-documenting. Reserve comments for "why," not "what."
|
|
752
|
+
|
|
753
|
+
```java
|
|
754
|
+
boolean isEligibleForDiscount =
|
|
755
|
+
user.isActive()
|
|
756
|
+
&& user.membershipYears() >= 2
|
|
757
|
+
&& !user.isBlacklisted()
|
|
758
|
+
&& order.total() > MINIMUM_DISCOUNT_THRESHOLD;
|
|
759
|
+
|
|
760
|
+
if (isEligibleForDiscount) {
|
|
761
|
+
applyDiscount(order);
|
|
762
|
+
}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
**Detection rule:**
|
|
766
|
+
|
|
767
|
+
Flag comments that begin with "check if," "this code does," or "the following." Flag functions where comment lines exceed code lines. Flag single-letter variable names adjacent to explanatory comments.
|
|
768
|
+
|
|
769
|
+
---
|
|
770
|
+
|
|
771
|
+
### AP-15: Stringly-Typed Code
|
|
772
|
+
|
|
773
|
+
**Also known as:** String Abuse, Type Erasure by Convention
|
|
774
|
+
**Frequency:** Common
|
|
775
|
+
**Severity:** High
|
|
776
|
+
**Detection difficulty:** Moderate
|
|
777
|
+
|
|
778
|
+
**What it looks like:**
|
|
779
|
+
|
|
780
|
+
Using strings where enums, types, or structured data would be safer.
|
|
781
|
+
|
|
782
|
+
```python
|
|
783
|
+
def move_robot(x: str, y: str, direction: str):
|
|
784
|
+
if direction == "up":
|
|
785
|
+
...
|
|
786
|
+
elif direction == "dwon": # typo: undetected
|
|
787
|
+
...
|
|
788
|
+
|
|
789
|
+
def process_event(event_type: str, payload: str):
|
|
790
|
+
if event_type == "user.created":
|
|
791
|
+
data = json.loads(payload)
|
|
792
|
+
...
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
**Why developers do it:**
|
|
796
|
+
|
|
797
|
+
Strings are universal. They work across serialization boundaries, require no type definitions, and are quick to add. "Just pass a string" is the fastest path to a working prototype.
|
|
798
|
+
|
|
799
|
+
**What goes wrong:**
|
|
800
|
+
|
|
801
|
+
Scott Hanselman coined "stringly typed" to contrast with "strongly typed." String-based dispatch has no compiler support: typos like `"dwon"` instead of `"down"` are valid strings that silently take the wrong branch. Every consumer must independently know and correctly spell the valid values. Refactoring is impossible without full-text search. In a Hacker News discussion (2024), developers documented production incidents where stringly-typed event systems silently dropped events because the string constants drifted between producer and consumer codebases.
|
|
802
|
+
|
|
803
|
+
**The fix:**
|
|
804
|
+
|
|
805
|
+
Replace strings with enums or algebraic types.
|
|
806
|
+
|
|
807
|
+
```python
|
|
808
|
+
class Direction(Enum):
|
|
809
|
+
UP = "up"
|
|
810
|
+
DOWN = "down"
|
|
811
|
+
LEFT = "left"
|
|
812
|
+
RIGHT = "right"
|
|
813
|
+
|
|
814
|
+
def move_robot(x: int, y: int, direction: Direction):
|
|
815
|
+
match direction:
|
|
816
|
+
case Direction.UP: ...
|
|
817
|
+
case Direction.DOWN: ...
|
|
818
|
+
# compiler warns about unhandled cases
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
**Detection rule:**
|
|
822
|
+
|
|
823
|
+
Flag `if x == "literal_string"` chains with 3+ branches. Flag method parameters of type `String` named `*type`, `*status`, `*mode`, `*action`, `*event`.
|
|
824
|
+
|
|
825
|
+
---
|
|
826
|
+
|
|
827
|
+
### AP-16: Message Chains / Train Wrecks
|
|
828
|
+
|
|
829
|
+
**Also known as:** Law of Demeter Violation, Dot-Dot-Dot
|
|
830
|
+
**Frequency:** Common
|
|
831
|
+
**Severity:** Medium
|
|
832
|
+
**Detection difficulty:** Easy
|
|
833
|
+
|
|
834
|
+
**What it looks like:**
|
|
835
|
+
|
|
836
|
+
A chain of method calls traversing an object graph: `a.getB().getC().getD().doSomething()`.
|
|
837
|
+
|
|
838
|
+
```java
|
|
839
|
+
String cityName = order
|
|
840
|
+
.getCustomer()
|
|
841
|
+
.getAddress()
|
|
842
|
+
.getCity()
|
|
843
|
+
.getName()
|
|
844
|
+
.toUpperCase();
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
**Why developers do it:**
|
|
848
|
+
|
|
849
|
+
The data is "right there," reachable by traversing the object graph. Creating intermediary methods feels like unnecessary indirection.
|
|
850
|
+
|
|
851
|
+
**What goes wrong:**
|
|
852
|
+
|
|
853
|
+
The calling code is now coupled to the entire chain structure. If `Address` is refactored to split `city` into `city` and `region`, every chain that traverses through `getCity()` breaks -- even code in modules that have no business knowing about address internals. Null/undefined at any link in the chain causes a NullPointerException at a location that reveals the entire internal structure to the error log. The code is also extremely difficult to mock in tests, as documented in industrial testing literature on "mock trainwrecks."
|
|
854
|
+
|
|
855
|
+
**The fix:**
|
|
856
|
+
|
|
857
|
+
Apply the Law of Demeter: "talk only to your immediate friends." Use delegation methods.
|
|
858
|
+
|
|
859
|
+
```java
|
|
860
|
+
// Order delegates to Customer, which delegates to Address
|
|
861
|
+
class Order {
|
|
862
|
+
String getShippingCity() {
|
|
863
|
+
return customer.getShippingCity();
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
class Customer {
|
|
868
|
+
String getShippingCity() {
|
|
869
|
+
return address.getCityName();
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// Caller
|
|
874
|
+
String city = order.getShippingCity();
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
**Detection rule:**
|
|
878
|
+
|
|
879
|
+
Flag chains of 3+ method calls on return values (excluding builders and fluent APIs). Tools: PMD `LawOfDemeter` rule.
|
|
880
|
+
|
|
881
|
+
---
|
|
882
|
+
|
|
883
|
+
### AP-17: Middle Man
|
|
884
|
+
|
|
885
|
+
**Also known as:** Thin Wrapper, Pass-Through Class, Bureaucrat
|
|
886
|
+
**Frequency:** Occasional
|
|
887
|
+
**Severity:** Medium
|
|
888
|
+
**Detection difficulty:** Moderate
|
|
889
|
+
|
|
890
|
+
**What it looks like:**
|
|
891
|
+
|
|
892
|
+
A class that delegates almost all its work to another class, adding no logic of its own.
|
|
893
|
+
|
|
894
|
+
```java
|
|
895
|
+
class CustomerService {
|
|
896
|
+
private CustomerRepository repo;
|
|
897
|
+
|
|
898
|
+
Customer find(int id) { return repo.find(id); }
|
|
899
|
+
void save(Customer c) { repo.save(c); }
|
|
900
|
+
void delete(int id) { repo.delete(id); }
|
|
901
|
+
List<Customer> findAll() { return repo.findAll(); }
|
|
902
|
+
// Every method is a one-line delegation
|
|
903
|
+
}
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
**Why developers do it:**
|
|
907
|
+
|
|
908
|
+
Architectural patterns (service layers, facades) encourage indirection. Developers create a "service" class because the pattern says to, even when there is no business logic to put there yet.
|
|
909
|
+
|
|
910
|
+
**What goes wrong:**
|
|
911
|
+
|
|
912
|
+
Every new repository method requires a corresponding pass-through in the service, doubling the maintenance surface. The middle man obscures the actual dependency, making navigation harder during debugging. When combined with speculative generality (AP-12), the codebase accumulates layers of empty indirection.
|
|
913
|
+
|
|
914
|
+
**The fix:**
|
|
915
|
+
|
|
916
|
+
If the class adds no logic, remove it and let callers use the delegate directly. If business logic will be added later, add the class when the logic arrives.
|
|
917
|
+
|
|
918
|
+
```java
|
|
919
|
+
// Before: callers -> CustomerService -> CustomerRepository
|
|
920
|
+
// After: callers -> CustomerRepository (until business logic is needed)
|
|
921
|
+
|
|
922
|
+
// When business logic appears, THEN introduce the service:
|
|
923
|
+
class CustomerService {
|
|
924
|
+
private CustomerRepository repo;
|
|
925
|
+
|
|
926
|
+
Customer findOrThrow(int id) {
|
|
927
|
+
return repo.find(id)
|
|
928
|
+
.orElseThrow(() -> new CustomerNotFoundException(id));
|
|
929
|
+
}
|
|
930
|
+
// Now the class earns its existence
|
|
931
|
+
}
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
**Detection rule:**
|
|
935
|
+
|
|
936
|
+
Flag classes where >80% of public methods are single-line delegations to the same field.
|
|
937
|
+
|
|
938
|
+
---
|
|
939
|
+
|
|
940
|
+
### AP-18: Refused Bequest
|
|
941
|
+
|
|
942
|
+
**Also known as:** Inheritance Misuse, Broken Liskov, Fake-Is-A
|
|
943
|
+
**Frequency:** Occasional
|
|
944
|
+
**Severity:** High
|
|
945
|
+
**Detection difficulty:** Moderate
|
|
946
|
+
|
|
947
|
+
**What it looks like:**
|
|
948
|
+
|
|
949
|
+
A subclass inherits from a parent but ignores, overrides with no-ops, or throws exceptions for inherited methods.
|
|
950
|
+
|
|
951
|
+
```python
|
|
952
|
+
class Bird:
|
|
953
|
+
def fly(self):
|
|
954
|
+
return "flying"
|
|
955
|
+
|
|
956
|
+
def eat(self):
|
|
957
|
+
return "eating"
|
|
958
|
+
|
|
959
|
+
class Penguin(Bird):
|
|
960
|
+
def fly(self):
|
|
961
|
+
raise NotImplementedError("Penguins can't fly") # refused bequest
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
**Why developers do it:**
|
|
965
|
+
|
|
966
|
+
The subclass needs some of the parent's functionality, and inheritance seems quicker than composition. "A penguin *is* a bird" feels logically correct, even though the behavioral contract breaks.
|
|
967
|
+
|
|
968
|
+
**What goes wrong:**
|
|
969
|
+
|
|
970
|
+
This violates the Liskov Substitution Principle: code that accepts `Bird` and calls `fly()` will crash on `Penguin`. The Java `Stack` class inheriting from `Vector` is a classic real-world refused bequest -- `Stack` inherits methods like `insertElementAt(index)` that violate stack semantics (you should not be able to insert in the middle of a stack). Clients using `Stack` as a `Vector` bypass LIFO ordering with no compiler warning.
|
|
971
|
+
|
|
972
|
+
**The fix:**
|
|
973
|
+
|
|
974
|
+
Replace inheritance with composition (delegation).
|
|
975
|
+
|
|
976
|
+
```python
|
|
977
|
+
class Bird:
|
|
978
|
+
def eat(self):
|
|
979
|
+
return "eating"
|
|
980
|
+
|
|
981
|
+
class FlyingBird(Bird):
|
|
982
|
+
def fly(self):
|
|
983
|
+
return "flying"
|
|
984
|
+
|
|
985
|
+
class Penguin(Bird):
|
|
986
|
+
# No fly() method -- does not promise what it cannot deliver
|
|
987
|
+
def swim(self):
|
|
988
|
+
return "swimming"
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
**Detection rule:**
|
|
992
|
+
|
|
993
|
+
Flag subclass methods that throw `NotImplementedError`, `UnsupportedOperationException`, or return `null`/no-op for inherited methods. Flag subclasses that override >50% of parent methods.
|
|
994
|
+
|
|
995
|
+
---
|
|
996
|
+
|
|
997
|
+
### AP-19: Data Class
|
|
998
|
+
|
|
999
|
+
**Also known as:** Anemic Domain Model, Dumb Container, Struct Syndrome
|
|
1000
|
+
**Frequency:** Common
|
|
1001
|
+
**Severity:** Medium
|
|
1002
|
+
**Detection difficulty:** Easy
|
|
1003
|
+
|
|
1004
|
+
**What it looks like:**
|
|
1005
|
+
|
|
1006
|
+
A class that contains only fields and getters/setters, with all behavior living in other classes.
|
|
1007
|
+
|
|
1008
|
+
```java
|
|
1009
|
+
class Customer {
|
|
1010
|
+
private String name;
|
|
1011
|
+
private String email;
|
|
1012
|
+
private List<Order> orders;
|
|
1013
|
+
// 15 getters and setters, zero behavior
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
class CustomerService {
|
|
1017
|
+
boolean isVip(Customer c) {
|
|
1018
|
+
return c.getOrders().stream()
|
|
1019
|
+
.mapToDouble(Order::getTotal)
|
|
1020
|
+
.sum() > 10000;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
String formatDisplayName(Customer c) {
|
|
1024
|
+
return c.getName() + " (" + c.getEmail() + ")";
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
**Why developers do it:**
|
|
1030
|
+
|
|
1031
|
+
Many frameworks (JPA, Hibernate, serialization libraries) encourage POJOs with getters/setters. MVC patterns place all logic in services. The "data class" feels clean and simple.
|
|
1032
|
+
|
|
1033
|
+
**What goes wrong:**
|
|
1034
|
+
|
|
1035
|
+
Martin Fowler calls this the "Anemic Domain Model" anti-pattern. Business logic that belongs with the data scatters across service classes, causing feature envy (AP-06) and shotgun surgery (AP-10). The data class exposes its internals, making it impossible to enforce invariants. Any service can set invalid state because the data class has no self-defense.
|
|
1036
|
+
|
|
1037
|
+
**The fix:**
|
|
1038
|
+
|
|
1039
|
+
Move behavior to the data class. Encapsulate invariants.
|
|
1040
|
+
|
|
1041
|
+
```java
|
|
1042
|
+
class Customer {
|
|
1043
|
+
private String name;
|
|
1044
|
+
private String email;
|
|
1045
|
+
private List<Order> orders;
|
|
1046
|
+
|
|
1047
|
+
boolean isVip() {
|
|
1048
|
+
return orders.stream()
|
|
1049
|
+
.mapToDouble(Order::getTotal)
|
|
1050
|
+
.sum() > VIP_THRESHOLD;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
String displayName() {
|
|
1054
|
+
return name + " (" + email + ")";
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
void addOrder(Order order) {
|
|
1058
|
+
Objects.requireNonNull(order);
|
|
1059
|
+
this.orders.add(order);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
```
|
|
1063
|
+
|
|
1064
|
+
**Detection rule:**
|
|
1065
|
+
|
|
1066
|
+
Flag classes where public methods are exclusively getters/setters. Flag classes with >5 fields and zero non-accessor methods.
|
|
1067
|
+
|
|
1068
|
+
---
|
|
1069
|
+
|
|
1070
|
+
### AP-20: Misleading / Outdated Comments
|
|
1071
|
+
|
|
1072
|
+
**Also known as:** Comment Rot, Lying Documentation, Stale Comments
|
|
1073
|
+
**Frequency:** Very Common
|
|
1074
|
+
**Severity:** High
|
|
1075
|
+
**Detection difficulty:** Very Hard
|
|
1076
|
+
|
|
1077
|
+
**What it looks like:**
|
|
1078
|
+
|
|
1079
|
+
Comments that describe behavior the code no longer exhibits, creating a trap for future developers.
|
|
1080
|
+
|
|
1081
|
+
```python
|
|
1082
|
+
def calculate_tax(amount):
|
|
1083
|
+
"""Calculates tax at 7% rate for domestic orders."""
|
|
1084
|
+
# Updated to 8.5% per 2024 regulation
|
|
1085
|
+
# Actually updated again to 9% for 2025
|
|
1086
|
+
return amount * 0.085 # Neither comment matches the code
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
**Why developers do it:**
|
|
1090
|
+
|
|
1091
|
+
Developers update code but forget to update comments. Comment maintenance has no enforcement mechanism. Code reviews focus on logic, not comment accuracy.
|
|
1092
|
+
|
|
1093
|
+
**What goes wrong:**
|
|
1094
|
+
|
|
1095
|
+
Research by Lin et al. (2024, arXiv:2409.10781) found that code-comment inconsistencies are approximately 1.5x more likely to appear in bug-introducing commits. A documented Mozilla incident showed that an outdated comment persisted across versions, leading a developer to build new functionality based on the documented (but no longer actual) behavior, resulting in a reported bug. In one case study, a developer trusting an outdated comment about pricing logic built a feature that relied on the documented behavior, producing a pricing bug that cost thousands of dollars before detection.
|
|
1096
|
+
|
|
1097
|
+
**The fix:**
|
|
1098
|
+
|
|
1099
|
+
Delete comments that describe "what." Keep comments that describe "why" (design decisions, regulatory constraints, non-obvious tradeoffs). Treat comment accuracy as a code review checklist item.
|
|
1100
|
+
|
|
1101
|
+
```python
|
|
1102
|
+
def calculate_tax(amount):
|
|
1103
|
+
"""Apply current tax rate per regulation XYZ-2025."""
|
|
1104
|
+
return amount * CURRENT_TAX_RATE # rate defined in config
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
**Detection rule:**
|
|
1108
|
+
|
|
1109
|
+
Flag TODO/FIXME comments older than 90 days. Flag functions where the docstring mentions different constants than the code uses. Automated: compare function names/docstrings against actual behavior using LLM-based analysis.
|
|
1110
|
+
|
|
1111
|
+
---
|
|
1112
|
+
|
|
1113
|
+
## Root Cause Analysis
|
|
1114
|
+
|
|
1115
|
+
| Root Cause | Contributing Smells | Systemic Fix |
|
|
1116
|
+
|---|---|---|
|
|
1117
|
+
| **Time pressure / sprint deadlines** | God Class, Long Method, Copy-Paste, Deep Nesting | Allocate refactoring budget per sprint (15-20% of velocity) |
|
|
1118
|
+
| **Missing domain modeling** | Primitive Obsession, Data Clumps, Stringly Typed, Magic Numbers | Domain-Driven Design workshops; value object patterns |
|
|
1119
|
+
| **Path of least resistance** | Feature Envy, Divergent Change, Dead Code | Architectural fitness functions; automated smell detection in CI |
|
|
1120
|
+
| **Fear of deletion** | Dead Code, Speculative Generality, Comments as Deodorant | Git history as backup; feature flag TTL enforcement |
|
|
1121
|
+
| **Framework-driven design** | Data Class, Middle Man, Shotgun Surgery | Challenge framework defaults; add behavior to domain objects |
|
|
1122
|
+
| **Misapplied patterns** | Speculative Generality, Middle Man, Refused Bequest | "Three strikes" rule before abstracting; favor composition |
|
|
1123
|
+
| **Inadequate code review** | Copy-Paste, Misleading Comments, Boolean Blindness | Smell-specific review checklists; automated reviewers (SonarQube, CodeClimate) |
|
|
1124
|
+
| **Solo development / knowledge silos** | God Class, Long Method, Magic Numbers | Pair programming; mob programming; mandatory review |
|
|
1125
|
+
| **Weak type system usage** | Primitive Obsession, Stringly Typed, Boolean Blindness | Strict compiler settings; custom lint rules for domain types |
|
|
1126
|
+
| **Incremental accumulation** | All smells | Track code health metrics over time; set quality gates |
|
|
1127
|
+
|
|
1128
|
+
## Self-Check Questions
|
|
1129
|
+
|
|
1130
|
+
1. **God Class:** Does any class in the codebase have more than 20 public methods or more than 500 lines? Can you describe its responsibility in a single sentence without using "and"?
|
|
1131
|
+
|
|
1132
|
+
2. **Copy-Paste:** If you fix a bug in this function, is there an identical block somewhere else that also needs the fix? Search for a distinctive line from the function -- do you get multiple hits?
|
|
1133
|
+
|
|
1134
|
+
3. **Primitive Obsession:** Are you passing raw `int`, `double`, or `String` values that represent domain concepts (money, distance, user IDs, status codes)? Could two values of the same primitive type be accidentally swapped?
|
|
1135
|
+
|
|
1136
|
+
4. **Long Method:** Can you read and understand this function without scrolling? Does it operate at a single level of abstraction, or does it mix HTTP parsing with business logic with database calls?
|
|
1137
|
+
|
|
1138
|
+
5. **Deep Nesting:** Is any code block nested more than 3 levels deep? Can you explain the conditions required to reach the innermost block without re-reading the function?
|
|
1139
|
+
|
|
1140
|
+
6. **Feature Envy:** Does this method access more fields from another class than from its own? Would it be simpler if it lived in the other class?
|
|
1141
|
+
|
|
1142
|
+
7. **Dead Code:** Are there functions with zero callers? Commented-out code blocks? Feature flags that shipped months ago and were never cleaned up?
|
|
1143
|
+
|
|
1144
|
+
8. **Shotgun Surgery:** When you add a new field to a domain object, how many files do you need to touch? Is the answer greater than 3?
|
|
1145
|
+
|
|
1146
|
+
9. **Boolean Blindness:** At any call site, can you understand what each `True`/`False` argument means without looking up the method signature?
|
|
1147
|
+
|
|
1148
|
+
10. **Magic Numbers:** If a business rule changes (tax rate, threshold, timeout), can you change it in exactly one place? Or must you grep for a numeric literal?
|
|
1149
|
+
|
|
1150
|
+
11. **Data Class:** Do any classes consist entirely of getters and setters? Is all the behavior in separate "service" classes that pull data out, process it, and push it back in?
|
|
1151
|
+
|
|
1152
|
+
12. **Speculative Generality:** Are there interfaces with exactly one implementation? Abstract classes with one subclass? Can you delete an abstraction layer and have everything still compile?
|
|
1153
|
+
|
|
1154
|
+
13. **Message Chains:** Do you see chains of 3+ method calls like `a.getB().getC().getD()`? If any intermediate object changes its structure, how many call sites break?
|
|
1155
|
+
|
|
1156
|
+
14. **Misleading Comments:** Pick any comment in the codebase. Does it accurately describe what the adjacent code does *right now*? When was the comment last updated relative to the code?
|
|
1157
|
+
|
|
1158
|
+
15. **Refused Bequest:** Do any subclasses override parent methods to throw exceptions or return no-ops? Could clients of the parent type be surprised by the subclass behavior?
|
|
1159
|
+
|
|
1160
|
+
## Code Smell Quick Reference
|
|
1161
|
+
|
|
1162
|
+
| Smell | AKA | Severity | Frequency | Key Signal | First Action |
|
|
1163
|
+
|---|---|---|---|---|---|
|
|
1164
|
+
| God Class | Blob | Critical | Very Common | >500 LOC, >20 methods | Extract Class |
|
|
1165
|
+
| Copy-Paste | Duplicated Code | Critical | Very Common | Identical blocks in 2+ places | Extract Method |
|
|
1166
|
+
| Primitive Obsession | Stringly Typed | Critical | Very Common | Raw primitives for domain concepts | Introduce Value Object |
|
|
1167
|
+
| Long Method | Mega-Function | High | Very Common | >30 LOC of logic | Extract Method |
|
|
1168
|
+
| Deep Nesting | Arrow Code | High | Very Common | >3 levels of indentation | Guard Clauses |
|
|
1169
|
+
| Feature Envy | Misplaced Method | High | Common | Method uses other class's data | Move Method |
|
|
1170
|
+
| Data Clumps | Parameter Clusters | Medium | Common | Same params in 3+ signatures | Introduce Parameter Object |
|
|
1171
|
+
| Magic Numbers | Unnamed Constants | Medium | Very Common | Literal numbers in logic | Extract Constant |
|
|
1172
|
+
| Dead Code | Zombie Code | Critical | Common | Zero callers, commented blocks | Delete (git is your backup) |
|
|
1173
|
+
| Shotgun Surgery | Scattered Change | High | Common | 5+ files changed for one feature | Move Method/Field |
|
|
1174
|
+
| Divergent Change | Swiss Army Class | High | Common | Class changed for unrelated reasons | Extract Class |
|
|
1175
|
+
| Speculative Generality | YAGNI Violation | Medium | Common | Interface with 1 implementation | Collapse Hierarchy |
|
|
1176
|
+
| Boolean Blindness | Boolean Trap | Medium | Common | `foo(true, false, true)` | Replace with Enum |
|
|
1177
|
+
| Comments as Deodorant | Explaining Bad Code | Medium | Very Common | Comment longer than code it explains | Rename + Extract |
|
|
1178
|
+
| Stringly Typed | String Abuse | High | Common | String comparison chains | Introduce Enum |
|
|
1179
|
+
| Message Chains | Train Wreck | Medium | Common | `a.b().c().d()` | Hide Delegate |
|
|
1180
|
+
| Middle Man | Pass-Through | Medium | Occasional | >80% one-line delegations | Remove Middle Man |
|
|
1181
|
+
| Refused Bequest | Broken Liskov | High | Occasional | Override with throw/no-op | Replace Inheritance with Delegation |
|
|
1182
|
+
| Data Class | Anemic Model | Medium | Common | All getters, no behavior | Move behavior to data class |
|
|
1183
|
+
| Misleading Comments | Comment Rot | High | Very Common | Comment contradicts code | Delete or update comment |
|
|
1184
|
+
|
|
1185
|
+
---
|
|
1186
|
+
*Researched: 2026-03-08 | Sources: Fowler, "Refactoring" (1999, 2018); Beck & Fowler code smell catalog; Apple goto fail CVE-2014-1266 (dwheeler.com); Mars Climate Orbiter mishap (NASA/JPL); Ariane 5 Flight 501 Inquiry Board Report; Knight Capital SEC filing and post-mortem (henricodolfing.ch); Heartbleed CVE-2014-0160 (heartbleed.com); Li et al. CP-Miner (UIUC); Lin et al. code-comment inconsistency study (arXiv:2409.10781); SonarSource cognitive complexity whitepaper; refactoring.guru; sourcemaking.com; luzkan.github.io/smells*
|